spki_commands.c 18.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* spki_commands.c
 *
 * SPKI interface
 *
 * $Id$ */

/* lsh, an implementation of the ssh protocol
 *
 * Copyright (C) 1999 Niels Mller
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "spki_commands.h"

28
29
30
#include "atoms.h"
#include "crypto.h"
#include "format.h"
Niels Möller's avatar
Niels Möller committed
31
#include "queue.h"
32
#include "randomness.h"
33
34
35
36
#include "sexp_commands.h"
#include "werror.h"
#include "xalloc.h"

37
38
#include <assert.h>

39
40
41
42
43
44
45
46
47
48
/* Forward declarations */
struct command_simple spki_add_acl_command;
#define SPKI_ADD_ACL (&spki_add_acl_command.super.super)

struct command_simple spki_return_hostkeys;
#define RETURN_HOSTKEYS (&spki_return_hostkeys.super.super)

struct command_simple spki_add_hostkey_command;
#define SPKI_ADD_HOSTKEY (&spki_add_hostkey_command.super.super)

49
50
51
struct command_simple spki_return_userkeys;
#define RETURN_USERKEYS (&spki_return_userkeys.super.super)

52
53
54
struct command_simple spki_add_userkey_command;
#define SPKI_ADD_USERKEY (&spki_add_userkey_command.super.super)

55

56
57
#include "spki_commands.c.x"

58
#define SA(x) sexp_a(ATOM_##x)
59

60
61
62
63
64
65
#define SPKI_ERROR(e, msg, expr) \
EXCEPTION_RAISE((e), make_spki_exception(EXC_SPKI_TYPE, (msg), (expr)))


/* Various conversion functions */

66
DEFINE_COMMAND_SIMPLE(spki_signer2verifier, a)
67
68
69
70
71
{
  CAST_SUBTYPE(signer, private, a);
  return &SIGNER_GET_VERIFIER(private)->super;
}

72
DEFINE_COMMAND_SIMPLE(spki_verifier2public, a)
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
{
  CAST_SUBTYPE(verifier, v, a);
  return &spki_make_public_key(v)->super;
}

/* Create an SPKI hash from an s-expression. */
/* GABA:
   (class
     (name spki_hash)
     (super command)
     (vars
       (name . int)
       (algorithm object hash_algorithm)))
*/

static void
do_spki_hash(struct command *s,
	     struct lsh_object *a,
	     struct command_continuation *c,
	     struct exception_handler *e UNUSED)
{
  CAST(spki_hash, self, s);
  CAST_SUBTYPE(sexp, o, a);

  struct lsh_string *tmp = hash_string(self->algorithm,
				       sexp_format(o, SEXP_CANONICAL, 0),
				       1);
  struct sexp *hash = spki_hash_data(self->algorithm, self->name, 
				     tmp->length, tmp->data);
  lsh_string_free(tmp);
  
  COMMAND_RETURN(c, hash);
}

struct command *
make_spki_hash(int name, struct hash_algorithm *algorithm)
{
  NEW(spki_hash, self);
  self->super.call = do_spki_hash;
  self->name = name;
  self->algorithm = algorithm;

  return &self->super;
}

const struct spki_hash spki_hash_md5 =
{ STATIC_COMMAND(do_spki_hash), ATOM_MD5, &md5_algorithm };

const struct spki_hash spki_hash_sha1 =
{ STATIC_COMMAND(do_spki_hash), ATOM_SHA1, &sha1_algorithm };


/* Reading keys */
  
/* Used for both sexp2keypair and sexp2signer. */

/* GABA:
   (class
     (name spki_parse_key)
     (super command)
     (vars
       (algorithms object alist)))
*/


static void
do_spki_sexp2signer(struct command *s, 
		    struct lsh_object *a,
		    struct command_continuation *c,
		    struct exception_handler *e)
{
  CAST(spki_parse_key, self, s);
  CAST_SUBTYPE(sexp, key, a);
  
  struct sexp_iterator *i;
  
  if (sexp_check_type(key, ATOM_PRIVATE_KEY, &i)) 
    {
      struct sexp *expr = SEXP_GET(i);
      struct signer *s;
      
      if (!expr)
	SPKI_ERROR(e, "spki.c: Invalid key.", key); 

      s = spki_make_signer(self->algorithms, expr, NULL);
      
      if (s)
	/* Test key here? */
	COMMAND_RETURN(c, s);
      else
	SPKI_ERROR(e, "spki.c: Invalid key.", expr); 
    }
  else
    SPKI_ERROR(e, "spki.c: Expected private-key expression.", key);
}

/* (parse algorithms sexp) -> signer */
170
DEFINE_COMMAND_SIMPLE(spki_sexp2signer_command, a)
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
{
  CAST_SUBTYPE(alist, algorithms, a);
  NEW(spki_parse_key, self);
  
  self->super.call = do_spki_sexp2signer;
  self->algorithms = algorithms;
  return &self->super.super;
}


static void 
parse_private_key(struct alist *algorithms,
                  struct sexp_iterator *i,
		  struct command_continuation *c,
		  struct exception_handler *e)
{
  struct sexp *expr = SEXP_GET(i);
  int algorithm_name;
  struct signer *s;
  
  if (!expr)
    {
      werror("parse_private_key: Invalid key.\n");
      SPKI_ERROR(e, "spki.c: Invalid key.", expr); 
      return;
    }

  s = spki_make_signer(algorithms, expr, &algorithm_name);

  if (!s)
    {
      SPKI_ERROR(e, "spki.c: Invalid key.", expr); 
      return;

    }

  /* Test key here? */  
  switch (algorithm_name)
    {	  
    case ATOM_DSA:
      COMMAND_RETURN(c, make_keypair(ATOM_SSH_DSS,
				     PUBLIC_KEY(SIGNER_GET_VERIFIER(s)),
				     s));
      /* Fall through */
    default:
      /* Get a corresponding public key. */
      COMMAND_RETURN(c, make_keypair
		     (ATOM_SPKI,
		      sexp_format(spki_make_public_key(SIGNER_GET_VERIFIER(s)),
				  SEXP_CANONICAL, 0),
		      s));

      break;
    }
}

static void
do_spki_sexp2keypair(struct command *s, 
		     struct lsh_object *a,
		     struct command_continuation *c,
		     struct exception_handler *e)
{
  CAST(spki_parse_key, self, s);
  CAST_SUBTYPE(sexp, key, a);
  
  struct sexp_iterator *i;
  
  switch (spki_get_type(key, &i)) 
    {
      default:
        SPKI_ERROR(e, "spki.c: Expected private-key expression.", key);
        return;
      case ATOM_PRIVATE_KEY:
	parse_private_key(self->algorithms, i, c, e);
	break;
    } 
}


/* (parse algorithms sexp) -> one or more keypairs */
251
DEFINE_COMMAND_SIMPLE(spki_sexp2keypair_command, a)
252
253
254
255
256
257
258
259
260
{
  CAST_SUBTYPE(alist, algorithms, a);
  NEW(spki_parse_key, self);
  
  self->super.call = do_spki_sexp2keypair;
  self->algorithms = algorithms;
  return &self->super.super;
}

261
262
263
264
265
266
267
268
269
270
271
/* GABA:
   (class
     (name spki_command)
     (super command)
     (vars
       (ctx object spki_context)))
*/

/* Reading of ACL:s
 * ****************/
 
272
/* Adds an ACL s-expression to an SPKI-context. Returns the context. */
273
274
275
276
277
278
279
280
281
282

static void
do_spki_add_acl(struct command *s,
		struct lsh_object *a,
		struct command_continuation *c,
		struct exception_handler *e UNUSED)
{
  CAST(spki_command, self, s);
  CAST_SUBTYPE(sexp, expr, a);

283
  trace("do_spki_add_acl\n");
284
285
286
287
288
  spki_add_acl(self->ctx, expr);

  COMMAND_RETURN(c, self->ctx);
}

289
DEFINE_COMMAND_SIMPLE(spki_add_acl_command, a)
290
291
292
293
294
295
296
{
  CAST_SUBTYPE(spki_context, ctx, a);

  NEW(spki_command, self);
  self->super.call = do_spki_add_acl;
  self->ctx = ctx;

297
298
  trace("spki_add_acl_command\n");

299
300
301
  return &self->super.super;
}

302
DEFINE_COMMAND_SIMPLE(spki_make_context_command, a)
303
304
{
  CAST_SUBTYPE(alist, algorithms, a);
305
306
  trace("spki_make_context_command\n");
  
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
  return &make_spki_context(algorithms)->super;
}


/* Reads a file of ACL:s, and returns an spki_context. */

/* GABA:
   (expr
     (name spki_read_acl)
     (params
       (algorithms object alist))
     (expr
       (lambda (file)
         (let ((ctx (spki_make_context
	              ;; Delay call, so that we really
		      ;; create one context for each file.
	              (prog1 algorithms file))))
	   (for_sexp
	     (lambda (e) ctx) ; Return ctx
  	     ;; Keep on reading until an SEXP_EOF or
	     ;; SEXP_SYNTAX exception is raised. 
328
329
	     (spki_add_acl ctx)
	     file)))))
330
331
332
333
334
335
336
337
338
339
340
*/

struct command *
make_spki_read_acls(struct alist *algorithms)
{
  CAST_SUBTYPE(command, res, spki_read_acl(algorithms));

  trace("make_spki_read_acl()\n");
  return res;
}

341
DEFINE_COMMAND_SIMPLE(spki_read_acls_command, a)
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
{
  CAST_SUBTYPE(alist, algorithms, a);
  CAST_SUBTYPE(command, res, spki_read_acl(algorithms));

  return &res->super;
}
  
/* Reading of host-keys
 * ********************/

/* GABA:
   (class
     (name spki_read_hostkey_context)
     (super command)
     (vars
       (keys object alist)))
*/

static void
do_spki_add_hostkey(struct command *s,
		    struct lsh_object *a,
		    struct command_continuation *c,
		    struct exception_handler *e UNUSED)
{
  CAST(spki_read_hostkey_context, self, s);
  CAST(keypair, key, a);

369
370
  trace("do_spki_add_hostkey\n");
  
371
372
373
374
375
376
377
378
379
  if (ALIST_GET(self->keys, key->type))
    werror("Multiple host keys of type %a.\n", key->type);
  else
    ALIST_SET(self->keys, key->type, key);

  COMMAND_RETURN(c, self->keys);
}

/* Ignores its argument */
380
DEFINE_COMMAND_SIMPLE(spki_add_hostkey_command, a)
381
382
{
  NEW(spki_read_hostkey_context, self);
383
384
385

  trace("spki_add_hostkey_command\n");

386
387
388
389
390
391
392
393
  (void) a;
  
  self->super.call = do_spki_add_hostkey;
  self->keys = make_alist(0, -1);

  return &self->super.super;
}     

394
DEFINE_COMMAND_SIMPLE(spki_return_hostkeys, a)
395
396
{
  CAST(spki_read_hostkey_context, self, a);
397
  trace("spki_return_hostkeys\n");
398
399
400
401
402
403
404
405
406
407
408
  return &self->keys->super;
}

/* GABA:
   (expr
     (name spki_read_hostkeys)
     (params
       (algorithms object alist))
     (expr
       (lambda (file)
         (let ((add (spki_add_hostkey file)))
409
410
411
           (for_sexp (lambda (e)
	   		;; Delay return until we actually get an exception
			(return_hostkeys (prog1 add e)))
412
	             (lambda (key)
413
		       (add (sexp2keypair
414
415
		               algorithms key)))
		     file)))))
416
417
*/

418
DEFINE_COMMAND_SIMPLE(spki_read_hostkeys_command, a)
419
420
421
422
{
  CAST_SUBTYPE(alist, algorithms, a);
  CAST_SUBTYPE(command, res, spki_read_hostkeys(algorithms));

423
424
  trace("spki_read_hostkeys_command\n");
  
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
  return &res->super;
}

/* Reading of private user-keys
 * ****************************/

/* GABA:
   (class
     (name spki_read_userkey_context)
     (super command)
     (vars
       (keys struct object_queue)))
*/

static void
do_spki_add_userkey(struct command *s,
		    struct lsh_object *a,
		    struct command_continuation *c,
		    struct exception_handler *e UNUSED)
{
  CAST(spki_read_userkey_context, self, s);
  CAST(keypair, key, a);

448
449
  trace("do_spki_add_userkey\n");
  
450
451
452
453
454
455
  object_queue_add_tail(&self->keys, &key->super);

  COMMAND_RETURN(c, s);
}

/* Ignores its argument */
456
DEFINE_COMMAND_SIMPLE(spki_add_userkey_command, a)
457
458
459
{
  NEW(spki_read_userkey_context, self);
  (void) a;
460
461

  trace("spki_add_userkey_command\n");
462
463
464
465
466
467
  self->super.call = do_spki_add_userkey;
  object_queue_init(&self->keys);

  return &self->super.super;
}     

468
DEFINE_COMMAND_SIMPLE(spki_return_userkeys, a)
469
470
{
  CAST(spki_read_userkey_context, self, a);
471
472
  trace("spki_return_userkeys\n");
  
473
474
475
476
477
478
479
480
481
482
483
  return &queue_to_list(&self->keys)->super.super;
}

/* GABA:
   (expr
     (name spki_read_userkeys)
     (params
       (algorithms object alist))
     (expr
       (lambda (file)
         (let ((ctx (spki_add_userkey file)))
484
485
486
           (for_sexp (lambda (e)
	   		;; Delay return until we actually get an exception
			(return_userkeys (prog1 ctx e)))
487
	             (lambda (key)
488
		       (ctx (sexp2keypair
489
490
		               algorithms key)))
		     file)))))
491
492
*/

493
494
struct command *
make_spki_read_userkeys(struct alist *algorithms)
495
496
{
  CAST_SUBTYPE(command, res, spki_read_userkeys(algorithms));
497
498
499
500
  trace("make_spki_read_userkeys\n");

  return res;
}
501

502
DEFINE_COMMAND_SIMPLE(spki_read_userkeys_command, a)
503
504
{
  CAST_SUBTYPE(alist, algorithms, a);
505
  
506
  return &make_spki_read_userkeys(algorithms)->super;
507
}
508
509
510
511
512


/* Encryption of private data.
 * For PKCS#5 (version 2) key derivation, we use
 *
513
514
515
516
 * (password-encrypted LABEL
 *   (Xpkcs5v2 hmac-sha1 (salt #...#)
 *                       (iterations #...#))
 *   ("3des-cbc" (iv #...#) (data #...#)))
517
518
519
520
521
522
523
524
525
526
527
528
 *
 * where the X:s will be removed when the format is more stable.
 *
 */

/* GABA:
   (class
     (name spki_password_encrypt)
     (super command)
     (vars
       (label string)
       (method object sexp)
529
       (algorithm_name . int)
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
       (algorithm object crypto_algorithm)
       (r object randomness)
       (key string)))
*/

static void
do_spki_encrypt(struct command *s,
		struct lsh_object *a,
		struct command_continuation *c,
		struct exception_handler *e UNUSED)
{
  CAST(spki_password_encrypt, self, s);
  CAST_SUBTYPE(sexp, expr, a);

  struct lsh_string *iv = NULL;
545
#if 0
546
  UINT8 noiv[1] = { 0 };
547
#endif
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
  
  if (self->algorithm->iv_size)
    {
      iv = lsh_string_alloc(self->algorithm->iv_size);
      RANDOM(self->r, iv->length, iv->data);
    }
  
  COMMAND_RETURN(c,
		 sexp_l(4,
			SA(PASSWORD_ENCRYPTED),
			sexp_s(NULL, lsh_string_dup(self->label)),
			self->method,
			sexp_l(3,
			       sexp_a(self->algorithm_name),
			       sexp_l(2, SA(IV), sexp_s(NULL, iv), -1),
			       sexp_l(2, SA(DATA),
				      sexp_s(NULL, crypt_string_pad
					     (MAKE_ENCRYPT(self->algorithm,
566
							   self->key->data, iv ? iv->data : NULL),
567
					      sexp_format(expr, SEXP_CANONICAL, 0), 1)),
568
569
570
571
572
573
574
575
576
577
578
				      -1),
			       -1),
			-1));
}

/* Consumes the label and password arguments. */
struct command *
make_pkcs5_encrypt(struct randomness *r,
		   struct lsh_string *label,
		   UINT32 prf_name,
		   struct mac_algorithm *prf,
579
		   int crypto_name,
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
		   struct crypto_algorithm *crypto,
		   UINT32 salt_length,
		   struct lsh_string *password,
		   UINT32 iterations)
{
  NEW(spki_password_encrypt, self);

  struct lsh_string *key;
  struct lsh_string *salt;
    
  assert(crypto);
  assert(prf);

  salt = lsh_string_alloc(salt_length);
  RANDOM(r, salt->length, salt->data);
    
  key = lsh_string_alloc(crypto->key_size);

  pkcs5_derive_key(prf,
		   password->length, password->data,
		   salt->length, salt->data,
		   iterations,
		   key->length, key->data);

  lsh_string_free(password);
  
  self->super.call = do_spki_encrypt;
  self->r = r;
  self->label = label;
609
610
  self->method = sexp_l(4, SA(XPKCS5V2), sexp_a(prf_name),
			sexp_l(2, SA(ITERATIONS), sexp_uint32(iterations), -1),
611
612
613
614
615
616
617
			sexp_l(2, SA(SALT), sexp_s(NULL, salt), -1), -1);
  self->algorithm_name = crypto_name;
  self->algorithm = crypto;
  self->key = key;

  return &self->super;
}
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840

/* GABA:
   (class
     (name spki_password_decrypt)
     (super command)
     (vars
       (mac_algorithms object alist)
       (crypto_algorithms object alist)
       (tty object interact)))
*/

static void
do_spki_decrypt(struct command *s,
		struct lsh_object *a,
		struct command_continuation *c,
		struct exception_handler *e)
{
  CAST(spki_password_decrypt, self, s);
  CAST_SUBTYPE(sexp, expr, a);

  struct sexp_iterator *i;
  
  if (!sexp_check_type(expr, ATOM_PASSWORD_ENCRYPTED, &i))
    COMMAND_RETURN(c, expr);

  else
    {
      struct lsh_string *label;
      struct sexp *key_info;
      struct sexp *payload;

      struct crypto_algorithm *crypto;
      struct mac_algorithm *mac;
      
      struct lsh_string *salt;
      struct lsh_string *iv;
      struct lsh_string *data;
      UINT32 iterations;
      
      if (SEXP_LEFT(i) != 3)
	{
	  SPKI_ERROR(e, "Invalid (password-encrypted ...) expression.",
		     expr);
	  return;
	}
	
      /* NOTE: This is a place where it might make sense to use a sexp
       * display type, but we don't support that for now. */
      label = sexp2string(SEXP_GET(i));

      if (!label)
	{
	  SPKI_ERROR(e, "Invalid label in (password-encrypted ...) expression.",
		     expr);
	  return;
	}

      SEXP_NEXT(i);
      key_info = SEXP_GET(i);
      assert(key_info);

      SEXP_NEXT(i);
      payload = SEXP_GET(i);
      assert(payload);

      /* Examine the payload expression first, before asking for a
       * pass phrase. */

      {
	int algorithm_name = spki_get_type(payload, &i);
	CAST_SUBTYPE(crypto_algorithm, tmp,
		     ALIST_GET(self->crypto_algorithms, algorithm_name));
	crypto = tmp;
      }

      if (!crypto)
	{
	  SPKI_ERROR(e, "Unknown encryption algorithm for pkcs5v2.", payload);
	  return;
	}

      iv = sexp2string(sexp_assq(i, ATOM_IV));

      if (crypto->iv_size)
	{
	  if (!iv || (iv->length != crypto->iv_size))
	    {
	      SPKI_ERROR(e, "Invalid IV for pkcs5v2.", payload);
	      return;
	    }
	}
      else if (iv)
	{
	  if (iv->length)
	    {
	      SPKI_ERROR(e, "Unexpected iv provided for pkcs5v2.", payload);
	      return;
	    }
	  iv = NULL;
	}
	
      data = sexp2string(sexp_assq(i, ATOM_DATA));

      if (!data)
	{
	  SPKI_ERROR(e, "Payload data missing for pkcs5v2.", payload);
	  return;
	}

      if (crypto->block_size && (data->length & crypto->block_size))
	{
	  SPKI_ERROR(e, "Payload data doesn't match block size for pkcs5v2.", payload);
	  return;
	}

      /* Get key */
      switch (spki_get_type(key_info, &i)) 
	{
	default:
	  SPKI_ERROR(e, "Unknown key derivation mechanism.", key_info);
	  return;

	case ATOM_XPKCS5V2:
	  if (SEXP_LEFT(i) != 3)
	    {
	      SPKI_ERROR(e, "Invalid pkcs5v2 parameters.", key_info);
	      return;
	    }
	  
	  {
	    int algorithm_name = sexp2atom(SEXP_GET(i));
	    
	    CAST_SUBTYPE(mac_algorithm, tmp,
			 ALIST_GET(self->mac_algorithms,
				   algorithm_name));

	    mac = tmp;
	  }

	  if (!mac)
	    {
	      SPKI_ERROR(e, "Unknown mac for pkcs5v2.", key_info);
	      return;
	    }

	  SEXP_NEXT(i);
	  if (!sexp2uint32(sexp_assq(i, ATOM_ITERATIONS), &iterations)
	      || !iterations)
	    {
	      SPKI_ERROR(e, "Invalid iteration count for pkcs5v2.", key_info);
	      return;
	    }
	    
	  salt = sexp2string(sexp_assq(i, ATOM_SALT));

	  if (!salt)
	    {
	      SPKI_ERROR(e, "Invalid salt for pkcs5v2.", key_info);
	      return;
	    }

	  /* Do the work */

	  {
	    struct lsh_string *password
	      = INTERACT_READ_PASSWORD(self->tty, 500, label, 0);
	    struct lsh_string *clear;
	    struct sexp *res;
	    UINT8 *key;
	    
	    if (!password)
	      {
		SPKI_ERROR(e, "No password provided for pkcs5v2.", key_info);
		return;
	      }

	    key = alloca(crypto->key_size);
	    pkcs5_derive_key(mac,
			     password->length, password->data,
			     salt->length, salt->data,
			     iterations,
			     crypto->key_size, key);

	    lsh_string_free(password);

	    clear = crypt_string_unpad(MAKE_DECRYPT(crypto,
						    key,
						    iv ? iv->data : NULL),
				       data, 0);

	    if (!clear)
	      {
		SPKI_ERROR(e, "Bad password for pkcs5v2.", key_info);
		return;
	      }

	    res = string_to_sexp(SEXP_CANONICAL, clear, 1);

	    if (res)
	      COMMAND_RETURN(c, res);
	    else
	      {
		SPKI_ERROR(e, "Bad password for pkcs5v2.", key_info);
		return;
	      }
	  }
	}
    }
}

struct command *
make_pkcs5_decrypt(struct alist *mac_algorithms,
		   struct alist *crypto_algorithms,
		   struct interact *tty)
{
  NEW(spki_password_decrypt, self);
  self->super.call = do_spki_decrypt;
  self->mac_algorithms = mac_algorithms;
  self->crypto_algorithms = crypto_algorithms;
  self->tty = tty;

  return &self->super;
}