1 /* compliance.c - Functions for compliance modi
2  * Copyright (C) 2017 g10 Code GmbH
3  * Copyright (C) 2017 Bundesamt für Sicherheit in der Informationstechnik
4  *
5  * This file is part of GnuPG.
6  *
7  * This file is free software; you can redistribute it and/or modify
8  * it under the terms of either
9  *
10  *   - the GNU Lesser General Public License as published by the Free
11  *     Software Foundation; either version 3 of the License, or (at
12  *     your option) any later version.
13  *
14  * or
15  *
16  *   - the GNU General Public License as published by the Free
17  *     Software Foundation; either version 2 of the License, or (at
18  *     your option) any later version.
19  *
20  * or both in parallel, as here.
21  *
22  * This file is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, see <https://www.gnu.org/licenses/>.
29  */
30 
31 #include <config.h>
32 #include <gcrypt.h>
33 
34 #include "openpgpdefs.h"
35 #include "logging.h"
36 #include "util.h"
37 #include "i18n.h"
38 #include "compliance.h"
39 
40 static int initialized;
41 static int module;
42 
43 
44 /* Return the address of a compliance cache variable for COMPLIANCE.
45  * If no such variable exists NULL is returned.  FOR_RNG returns the
46  * cache variable for the RNG compliance check. */
47 static int *
get_compliance_cache(enum gnupg_compliance_mode compliance,int for_rng)48 get_compliance_cache (enum gnupg_compliance_mode compliance, int for_rng)
49 {
50   static int r_gnupg   = -1, s_gnupg   = -1;
51   static int r_rfc4880 = -1, s_rfc4880 = -1;
52   static int r_rfc2440 = -1, s_rfc2440 = -1;
53   static int r_pgp7    = -1, s_pgp7    = -1;
54   static int r_pgp8    = -1, s_pgp8    = -1;
55   static int r_de_vs   = -1, s_de_vs   = -1;
56 
57   int *ptr = NULL;
58 
59   switch (compliance)
60     {
61     case CO_GNUPG:   ptr = for_rng? &r_gnupg   : &s_gnupg  ; break;
62     case CO_RFC4880: ptr = for_rng? &r_rfc4880 : &s_rfc4880; break;
63     case CO_RFC2440: ptr = for_rng? &r_rfc2440 : &s_rfc2440; break;
64     case CO_PGP7:    ptr = for_rng? &r_pgp7    : &s_pgp7   ; break;
65     case CO_PGP8:    ptr = for_rng? &r_pgp8    : &s_pgp8   ; break;
66     case CO_DE_VS:   ptr = for_rng? &r_de_vs   : &s_de_vs  ; break;
67     }
68 
69   return ptr;
70 }
71 
72 
73 /* Initializes the module.  Must be called with the current
74  * GNUPG_MODULE_NAME.  Checks a few invariants, and tunes the policies
75  * for the given module.  */
76 void
gnupg_initialize_compliance(int gnupg_module_name)77 gnupg_initialize_compliance (int gnupg_module_name)
78 {
79   log_assert (! initialized);
80 
81   /* We accept both OpenPGP-style and gcrypt-style algorithm ids.
82    * Assert that they are compatible.  */
83   log_assert ((int) GCRY_PK_RSA          == (int) PUBKEY_ALGO_RSA);
84   log_assert ((int) GCRY_PK_RSA_E        == (int) PUBKEY_ALGO_RSA_E);
85   log_assert ((int) GCRY_PK_RSA_S        == (int) PUBKEY_ALGO_RSA_S);
86   log_assert ((int) GCRY_PK_ELG_E        == (int) PUBKEY_ALGO_ELGAMAL_E);
87   log_assert ((int) GCRY_PK_DSA          == (int) PUBKEY_ALGO_DSA);
88   log_assert ((int) GCRY_PK_ECC          == (int) PUBKEY_ALGO_ECDH);
89   log_assert ((int) GCRY_PK_ELG          == (int) PUBKEY_ALGO_ELGAMAL);
90   log_assert ((int) GCRY_CIPHER_NONE     == (int) CIPHER_ALGO_NONE);
91   log_assert ((int) GCRY_CIPHER_IDEA     == (int) CIPHER_ALGO_IDEA);
92   log_assert ((int) GCRY_CIPHER_3DES     == (int) CIPHER_ALGO_3DES);
93   log_assert ((int) GCRY_CIPHER_CAST5    == (int) CIPHER_ALGO_CAST5);
94   log_assert ((int) GCRY_CIPHER_BLOWFISH == (int) CIPHER_ALGO_BLOWFISH);
95   log_assert ((int) GCRY_CIPHER_AES      == (int) CIPHER_ALGO_AES);
96   log_assert ((int) GCRY_CIPHER_AES192   == (int) CIPHER_ALGO_AES192);
97   log_assert ((int) GCRY_CIPHER_AES256   == (int) CIPHER_ALGO_AES256);
98   log_assert ((int) GCRY_CIPHER_TWOFISH  == (int) CIPHER_ALGO_TWOFISH);
99   log_assert ((int) GCRY_MD_MD5          == (int) DIGEST_ALGO_MD5);
100   log_assert ((int) GCRY_MD_SHA1         == (int) DIGEST_ALGO_SHA1);
101   log_assert ((int) GCRY_MD_RMD160       == (int) DIGEST_ALGO_RMD160);
102   log_assert ((int) GCRY_MD_SHA256       == (int) DIGEST_ALGO_SHA256);
103   log_assert ((int) GCRY_MD_SHA384       == (int) DIGEST_ALGO_SHA384);
104   log_assert ((int) GCRY_MD_SHA512       == (int) DIGEST_ALGO_SHA512);
105   log_assert ((int) GCRY_MD_SHA224       == (int) DIGEST_ALGO_SHA224);
106 
107   switch (gnupg_module_name)
108     {
109     case GNUPG_MODULE_NAME_GPGSM:
110     case GNUPG_MODULE_NAME_GPG:
111       break;
112 
113     default:
114       log_assert (!"no policies for this module");
115     }
116 
117   module = gnupg_module_name;
118   initialized = 1;
119 }
120 
121 /* Return true if ALGO with a key of KEYLENGTH is compliant to the
122  * given COMPLIANCE mode.  If KEY is not NULL, various bits of
123  * information will be extracted from it.  If CURVENAME is not NULL, it
124  * is assumed to be the already computed.  ALGO may be either an
125  * OpenPGP-style pubkey_algo_t, or a gcrypt-style enum gcry_pk_algos,
126  * both are compatible from the point of view of this function.  */
127 int
gnupg_pk_is_compliant(enum gnupg_compliance_mode compliance,int algo,unsigned int algo_flags,gcry_mpi_t key[],unsigned int keylength,const char * curvename)128 gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo,
129                        unsigned int algo_flags,
130 		       gcry_mpi_t key[], unsigned int keylength,
131                        const char *curvename)
132 {
133   enum { is_rsa, is_dsa, is_elg, is_ecc } algotype;
134   int result = 0;
135 
136   if (! initialized)
137     return 0;
138 
139   switch (algo)
140     {
141     case PUBKEY_ALGO_RSA:
142     case PUBKEY_ALGO_RSA_E:
143     case PUBKEY_ALGO_RSA_S:
144       algotype = is_rsa;
145       break;
146 
147     case PUBKEY_ALGO_DSA:
148       algotype = is_dsa;
149       break;
150 
151     case PUBKEY_ALGO_ELGAMAL_E:
152       algotype = is_elg;
153       break;
154 
155     case PUBKEY_ALGO_ECDH:
156     case PUBKEY_ALGO_ECDSA:
157     case PUBKEY_ALGO_EDDSA:
158       algotype = is_ecc;
159       break;
160 
161     case PUBKEY_ALGO_ELGAMAL:
162       return 0; /* Signing with Elgamal is not at all supported.  */
163 
164     default: /* Unknown.  */
165       return 0;
166     }
167 
168   if (compliance == CO_DE_VS)
169     {
170       char *curve = NULL;
171 
172       switch (algotype)
173         {
174         case is_elg:
175           result = 0;
176           break;
177 
178         case is_rsa:
179           result = (keylength == 2048
180                     || keylength == 3072
181                     || keylength == 4096);
182           /* Although rsaPSS was not part of the original evaluation
183            * we got word that we can claim compliance.  */
184           (void)algo_flags;
185           break;
186 
187 	case is_dsa:
188 	  if (key)
189 	    {
190 	      size_t P = gcry_mpi_get_nbits (key[0]);
191 	      size_t Q = gcry_mpi_get_nbits (key[1]);
192 	      result = (Q == 256
193 			&& (P == 2048 || P == 3072));
194 	    }
195 	  break;
196 
197         case is_ecc:
198           if (!curvename && key)
199             {
200               curve = openpgp_oid_to_str (key[0]);
201               curvename = openpgp_oid_to_curve (curve, 0);
202               if (!curvename)
203                 curvename = curve;
204             }
205 
206           result = (curvename
207                     && (algo == PUBKEY_ALGO_ECDH
208                         || algo == PUBKEY_ALGO_ECDSA)
209                     && (!strcmp (curvename, "brainpoolP256r1")
210                         || !strcmp (curvename, "brainpoolP384r1")
211                         || !strcmp (curvename, "brainpoolP512r1")));
212           break;
213 
214         default:
215           result = 0;
216         }
217       xfree (curve);
218     }
219   else
220     {
221       result = 1; /* Assume compliance.  */
222     }
223 
224   return result;
225 }
226 
227 
228 /* Return true if ALGO with the given KEYLENGTH is allowed in the
229  * given COMPLIANCE mode.  USE specifies for which use case the
230  * predicate is evaluated.  This way policies can be strict in what
231  * they produce, and liberal in what they accept.  */
232 int
gnupg_pk_is_allowed(enum gnupg_compliance_mode compliance,enum pk_use_case use,int algo,unsigned int algo_flags,gcry_mpi_t key[],unsigned int keylength,const char * curvename)233 gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance,
234 		     enum pk_use_case use, int algo,
235                      unsigned int algo_flags, gcry_mpi_t key[],
236 		     unsigned int keylength, const char *curvename)
237 {
238   int result = 0;
239 
240   if (! initialized)
241     return 1;
242 
243   switch (compliance)
244     {
245     case CO_DE_VS:
246       switch (algo)
247 	{
248 	case PUBKEY_ALGO_RSA:
249 	case PUBKEY_ALGO_RSA_E:
250 	case PUBKEY_ALGO_RSA_S:
251 	  switch (use)
252 	    {
253 	    case PK_USE_DECRYPTION:
254 	    case PK_USE_VERIFICATION:
255 	      result = 1;
256               break;
257 	    case PK_USE_ENCRYPTION:
258 	    case PK_USE_SIGNING:
259 	      result = (keylength == 2048
260                         || keylength == 3072
261                         || keylength == 4096);
262               break;
263 	    default:
264 	      log_assert (!"reached");
265 	    }
266           (void)algo_flags;
267 	  break;
268 
269 	case PUBKEY_ALGO_DSA:
270           if (use == PK_USE_VERIFICATION)
271             result = 1;
272 	  else if (use == PK_USE_SIGNING && key)
273 	    {
274 	      size_t P = gcry_mpi_get_nbits (key[0]);
275 	      size_t Q = gcry_mpi_get_nbits (key[1]);
276 	      result = (Q == 256 && (P == 2048 || P == 3072));
277             }
278           break;
279 
280 	case PUBKEY_ALGO_ELGAMAL:
281 	case PUBKEY_ALGO_ELGAMAL_E:
282 	  result = (use == PK_USE_DECRYPTION);
283           break;
284 
285 	case PUBKEY_ALGO_ECDH:
286 	  if (use == PK_USE_DECRYPTION)
287             result = 1;
288           else if (use == PK_USE_ENCRYPTION)
289             {
290               char *curve = NULL;
291 
292               if (!curvename && key)
293                 {
294                   curve = openpgp_oid_to_str (key[0]);
295                   curvename = openpgp_oid_to_curve (curve, 0);
296                   if (!curvename)
297                     curvename = curve;
298                 }
299 
300               result = (curvename
301                         && (!strcmp (curvename, "brainpoolP256r1")
302                             || !strcmp (curvename, "brainpoolP384r1")
303                             || !strcmp (curvename, "brainpoolP512r1")));
304 
305               xfree (curve);
306             }
307           break;
308 
309 	case PUBKEY_ALGO_ECDSA:
310           if (use == PK_USE_VERIFICATION)
311             result = 1;
312           else
313             {
314               char *curve = NULL;
315 
316               if (! curvename && key)
317 	      {
318 		curve = openpgp_oid_to_str (key[0]);
319 		curvename = openpgp_oid_to_curve (curve, 0);
320 		if (!curvename)
321 		  curvename = curve;
322 	      }
323 
324               result = (use == PK_USE_SIGNING
325                          && curvename
326                          && (!strcmp (curvename, "brainpoolP256r1")
327                              || !strcmp (curvename, "brainpoolP384r1")
328                              || !strcmp (curvename, "brainpoolP512r1")));
329               xfree (curve);
330             }
331           break;
332 
333 
334 	case PUBKEY_ALGO_EDDSA:
335 	  break;
336 
337 	default:
338 	  break;
339 	}
340       break;
341 
342     default:
343       /* The default policy is to allow all algorithms.  */
344       result = 1;
345     }
346 
347   return result;
348 }
349 
350 
351 /* Return true if (CIPHER, MODE) is compliant to the given COMPLIANCE mode.  */
352 int
gnupg_cipher_is_compliant(enum gnupg_compliance_mode compliance,cipher_algo_t cipher,enum gcry_cipher_modes mode)353 gnupg_cipher_is_compliant (enum gnupg_compliance_mode compliance,
354 			   cipher_algo_t cipher,
355 			   enum gcry_cipher_modes mode)
356 {
357   if (! initialized)
358     return 0;
359 
360   switch (compliance)
361     {
362     case CO_DE_VS:
363       switch (cipher)
364 	{
365 	case CIPHER_ALGO_AES:
366 	case CIPHER_ALGO_AES192:
367 	case CIPHER_ALGO_AES256:
368 	case CIPHER_ALGO_3DES:
369 	  switch (module)
370 	    {
371 	    case GNUPG_MODULE_NAME_GPG:
372 	      return mode == GCRY_CIPHER_MODE_CFB;
373 	    case GNUPG_MODULE_NAME_GPGSM:
374 	      return mode == GCRY_CIPHER_MODE_CBC;
375 	    }
376 	  log_assert (!"reached");
377 
378 	default:
379 	  return 0;
380 	}
381       log_assert (!"reached");
382 
383     default:
384       return 0;
385     }
386 
387   log_assert (!"reached");
388 }
389 
390 
391 /* Return true if CIPHER is allowed in the given COMPLIANCE mode.  If
392  * PRODUCER is true, the predicate is evaluated for the producer, if
393  * false for the consumer.  This way policies can be strict in what
394  * they produce, and liberal in what they accept.  */
395 int
gnupg_cipher_is_allowed(enum gnupg_compliance_mode compliance,int producer,cipher_algo_t cipher,enum gcry_cipher_modes mode)396 gnupg_cipher_is_allowed (enum gnupg_compliance_mode compliance, int producer,
397 			 cipher_algo_t cipher,
398 			 enum gcry_cipher_modes mode)
399 {
400   if (! initialized)
401     return 1;
402 
403   switch (compliance)
404     {
405     case CO_DE_VS:
406       switch (cipher)
407 	{
408 	case CIPHER_ALGO_AES:
409 	case CIPHER_ALGO_AES192:
410 	case CIPHER_ALGO_AES256:
411 	case CIPHER_ALGO_3DES:
412 	  switch (module)
413 	    {
414 	    case GNUPG_MODULE_NAME_GPG:
415 	      return (mode == GCRY_CIPHER_MODE_NONE
416                       || mode == GCRY_CIPHER_MODE_CFB);
417 	    case GNUPG_MODULE_NAME_GPGSM:
418 	      return (mode == GCRY_CIPHER_MODE_NONE
419                       || mode == GCRY_CIPHER_MODE_CBC
420                       || (mode == GCRY_CIPHER_MODE_GCM && !producer));
421 	    }
422 	  log_assert (!"reached");
423 
424 	case CIPHER_ALGO_BLOWFISH:
425 	case CIPHER_ALGO_CAMELLIA128:
426 	case CIPHER_ALGO_CAMELLIA192:
427 	case CIPHER_ALGO_CAMELLIA256:
428 	case CIPHER_ALGO_CAST5:
429 	case CIPHER_ALGO_IDEA:
430 	case CIPHER_ALGO_TWOFISH:
431 	  return (module == GNUPG_MODULE_NAME_GPG
432 		  && (mode == GCRY_CIPHER_MODE_NONE
433                       || mode == GCRY_CIPHER_MODE_CFB)
434 		  && ! producer);
435 	default:
436 	  return 0;
437 	}
438       log_assert (!"reached");
439 
440     default:
441       /* The default policy is to allow all algorithms.  */
442       return 1;
443     }
444 
445   log_assert (!"reached");
446 }
447 
448 
449 /* Return true if DIGEST is compliant to the given COMPLIANCE mode.  */
450 int
gnupg_digest_is_compliant(enum gnupg_compliance_mode compliance,digest_algo_t digest)451 gnupg_digest_is_compliant (enum gnupg_compliance_mode compliance,
452                            digest_algo_t digest)
453 {
454   if (! initialized)
455     return 0;
456 
457   switch (compliance)
458     {
459     case CO_DE_VS:
460       switch (digest)
461 	{
462 	case DIGEST_ALGO_SHA256:
463 	case DIGEST_ALGO_SHA384:
464 	case DIGEST_ALGO_SHA512:
465 	  return 1;
466 	default:
467 	  return 0;
468 	}
469       log_assert (!"reached");
470 
471     default:
472       return 0;
473     }
474 
475   log_assert (!"reached");
476 }
477 
478 
479 /* Return true if DIGEST is allowed in the given COMPLIANCE mode.  If
480  * PRODUCER is true, the predicate is evaluated for the producer, if
481  * false for the consumer.  This way policies can be strict in what
482  * they produce, and liberal in what they accept.  */
483 int
gnupg_digest_is_allowed(enum gnupg_compliance_mode compliance,int producer,digest_algo_t digest)484 gnupg_digest_is_allowed (enum gnupg_compliance_mode compliance, int producer,
485 			 digest_algo_t digest)
486 {
487   if (! initialized)
488     return 1;
489 
490   switch (compliance)
491     {
492     case CO_DE_VS:
493       switch (digest)
494 	{
495 	case DIGEST_ALGO_SHA256:
496 	case DIGEST_ALGO_SHA384:
497 	case DIGEST_ALGO_SHA512:
498 	  return 1;
499 	case DIGEST_ALGO_SHA1:
500 	case DIGEST_ALGO_SHA224:
501 	case DIGEST_ALGO_RMD160:
502 	  return ! producer;
503 	case DIGEST_ALGO_MD5:
504 	  return ! producer && module == GNUPG_MODULE_NAME_GPGSM;
505 	default:
506 	  return 0;
507 	}
508       log_assert (!"reached");
509 
510     default:
511       /* The default policy is to allow all algorithms.  */
512       return 1;
513     }
514 
515   log_assert (!"reached");
516 }
517 
518 
519 /* Return True if the random number generator is compliant in
520  * COMPLIANCE mode.  */
521 int
gnupg_rng_is_compliant(enum gnupg_compliance_mode compliance)522 gnupg_rng_is_compliant (enum gnupg_compliance_mode compliance)
523 {
524   int *result;
525   int res;
526 
527   result = get_compliance_cache (compliance, 1);
528 
529   if (result && *result != -1)
530     res = *result; /* Use cached result.  */
531   else if (compliance == CO_DE_VS)
532     {
533       /* We also check whether the library is at all compliant.  */
534       res = gnupg_gcrypt_is_compliant (compliance);
535 
536       /* In DE_VS mode under Windows we also require that the JENT RNG
537        * is active.  Check it here. */
538 #ifdef HAVE_W32_SYSTEM
539       if (res == 1)
540         {
541           char *buf;
542           const char *fields[5];
543 
544           buf = gcry_get_config (0, "rng-type");
545           if (buf
546               && split_fields_colon (buf, fields, DIM (fields)) >= 5
547               && atoi (fields[4]) > 0)
548             ; /* Field 5 > 0 := Jent is active.  */
549           else
550             result = 0;  /* Force non-compliance.  */
551           gcry_free (buf);
552         }
553 #endif /*HAVE_W32_SYSTEM*/
554     }
555   else
556     res = 1;
557 
558   if (result)
559     *result = res;
560 
561   return res;
562 }
563 
564 
565 /* Return true if the used Libgcrypt is compliant in COMPLIANCE
566  * mode.  */
567 int
gnupg_gcrypt_is_compliant(enum gnupg_compliance_mode compliance)568 gnupg_gcrypt_is_compliant (enum gnupg_compliance_mode compliance)
569 {
570   int *result;
571   int res;
572 
573   result = get_compliance_cache (compliance, 0);
574 
575   if (result && *result != -1)
576     res = *result; /* Use cached result.  */
577   else if (compliance == CO_DE_VS)
578     {
579       int is19orlater = !!gcry_check_version ("1.9.0");
580 
581       /* A compliant version of GnuPG requires Libgcrypt >= 1.8.1 and
582        * less than 1.9.0.  Version 1.9.0 requires a re-evaluation and
583        * can thus not be used for de-vs.  */
584       if (gcry_check_version ("1.8.1") && !is19orlater)
585         res = 1;  /* Compliant version of Libgcrypt.  */
586       else if (is19orlater)
587         {
588           /* Libgcrypt might be nice enough to tell us whether it is
589            * compliant.  */
590           char *buf;
591           const char *fields[3];
592 
593           buf = gcry_get_config (0, "compliance");
594           if (buf
595               && split_fields_colon (buf, fields, DIM (fields)) >= 2
596               && strstr (fields[1], "de-vs"))
597             res = 1;  /* Compliant.  */
598           else
599             res = 0;  /* Non-compliant.  */
600           gcry_free (buf);
601         }
602       else
603         res = 0;  /* Non-compliant version of Libgcrypt.  */
604     }
605   else
606     res = 1;
607 
608   if (result)
609     *result = res;
610 
611   return res;
612 }
613 
614 
615 const char *
gnupg_status_compliance_flag(enum gnupg_compliance_mode compliance)616 gnupg_status_compliance_flag (enum gnupg_compliance_mode compliance)
617 {
618   switch (compliance)
619     {
620     case CO_GNUPG:
621       return "8";
622     case CO_RFC4880:
623     case CO_RFC2440:
624     case CO_PGP7:
625     case CO_PGP8:
626       log_assert (!"no status code assigned for this compliance mode");
627     case CO_DE_VS:
628       return "23";
629     }
630   log_assert (!"invalid compliance mode");
631 }
632 
633 
634 /* Parse the value of --compliance.  Returns the value corresponding
635  * to the given STRING according to OPTIONS of size LENGTH, or -1
636  * indicating that the lookup was unsuccessful, or the list of options
637  * was printed.  If quiet is false, an additional hint to use 'help'
638  * is printed on unsuccessful lookups.  */
639 int
gnupg_parse_compliance_option(const char * string,struct gnupg_compliance_option options[],size_t length,int quiet)640 gnupg_parse_compliance_option (const char *string,
641 			       struct gnupg_compliance_option options[],
642 			       size_t length,
643 			       int quiet)
644 {
645   size_t i;
646 
647   if (! ascii_strcasecmp (string, "help"))
648     {
649       log_info (_("valid values for option '%s':\n"), "--compliance");
650       for (i = 0; i < length; i++)
651         log_info ("  %s\n", options[i].keyword);
652       return -1;
653     }
654 
655   for (i = 0; i < length; i++)
656     if (! ascii_strcasecmp (string, options[i].keyword))
657       return options[i].value;
658 
659   log_error (_("invalid value for option '%s'\n"), "--compliance");
660   if (! quiet)
661     log_info (_("(use \"help\" to list choices)\n"));
662   return -1;
663 }
664 
665 
666 /* Return the command line option for the given COMPLIANCE mode.  */
667 const char *
gnupg_compliance_option_string(enum gnupg_compliance_mode compliance)668 gnupg_compliance_option_string (enum gnupg_compliance_mode compliance)
669 {
670   switch (compliance)
671     {
672     case CO_GNUPG:   return "--compliance=gnupg";
673     case CO_RFC4880: return "--compliance=openpgp";
674     case CO_RFC2440: return "--compliance=rfc2440";
675     case CO_PGP7:    return "--compliance=pgp7";
676     case CO_PGP8:    return "--compliance=pgp8";
677     case CO_DE_VS:   return "--compliance=de-vs";
678     }
679 
680   log_assert (!"invalid compliance mode");
681 }
682