gpgsm: Allow specification of an AuthorityKeyIdentifier.
[gnupg.git] / sm / certreqgen.c
index 7d0bfbd..de7c39c 100644 (file)
  */
 
 /*
-The format of the native parameter file is follows:
-  o Text only, line length is limited to about 1000 chars.
-  o You must use UTF-8 encoding to specify non-ascii characters.
-  o Empty lines are ignored.
-  o Leading and trailing spaces are ignored.
-  o A hash sign as the first non white space character is a comment line.
-  o Control statements are indicated by a leading percent sign, the
-    arguments are separated by white space from the keyword.
-  o Parameters are specified by a keyword, followed by a colon.  Arguments
-    are separated by white space.
-  o The first parameter must be "Key-Type", control statements
-    may be placed anywhere.
-  o Key generation takes place when either the end of the parameter file
-    is reached, the next "Key-Type" parameter is encountered or at the
-    controlstatement "%commit"
-  o Control statements:
-    %echo <text>
-       Print <text>.
-    %dry-run
-       Suppress actual key generation (useful for syntax checking).
-    %commit
-       Perform the key generation.  Note that an implicit commit is done
-       at the next "Key-Type" parameter.
-    %certfile <filename>
-        [Not yet implemented!]
-       Do not write the certificate to the keyDB but to <filename>.
-        This must be given before the first
-       commit to take place, duplicate specification of the same filename
-       is ignored, the last filename before a commit is used.
-       The filename is used until a new filename is used (at commit points)
-       and all keys are written to that file.  If a new filename is given,
-       this file is created (and overwrites an existing one).
-       Both control statements must be given.
-
-   o The order of the parameters does not matter except for "Key-Type"
-     which must be the first parameter.  The parameters are only for the
-     generated keyblock and parameters from previous key generations are not
-     used. Some syntactically checks may be performed.
-
-     The currently defined parameters are:
-
-     Key-Type: <algo>
-       Starts a new parameter block by giving the type of the
-       primary key. The algorithm must be capable of signing.
-       This is a required parameter.  For now the only supported
-        algorithm is "rsa".
-
-     Key-Length: <length-in-bits>
-       Length of the key in bits.  Default is 2048.
-
-     Key-Grip: <hexstring>
-        This is optional and used to generate a request for an already
-        existing key.  Key-Length will be ignored when given,
-
-     Key-Usage: <usage-list>
-        Space or comma delimited list of key usage, allowed values are
-        "encrypt" and "sign".  This is used to generate the KeyUsage extension.
-        Please make sure that the algorithm is capable of this usage.  Default
-        is to allow encrypt and sign.
-
-     Name-DN: <subject_name>
-        This is the DN name of the subject in rfc2253 format.
-
-     Name-Email: <string>
-       The is an email address for the altSubjectName
-
-     Name-DNS: <string>
-       The is an DNS name for the altSubjectName
-
-     Name-URI: <string>
-       The is an URI for the altSubjectName
-
-     The following parameters are only used if a certificate (and not
-     a certificate signing request) is requested:
-
-     Serial: <sn>
-        If this parameter is given an X.509 certificate will be
-        generated.  SN is expected to be a hex string representing an
-        unsigned integer of arbitary length.  The special value
-        "random" can be used to crete a 64 bit random serial number.
-
-     Issuer-DN: <issuer_name>
-        This is the DN name of the issuer in rfc2253 format.  If it is
-        not set the subject DN will be used instead.  This creates a
-        self-signed certificate.  Only in this case a special GnuPG
-        extension will then be included in the certificate to mark it
-        as a standalone certificate.
-
-     Creation-Date: <iso-date>
-        Set the notBefore date of the certificate.  Either a date like
-        "1986-04-26" or a full timestamp like "19860426T042640" may be
-        used.  The time is considered to be UTC.  If it is not given
-        the current date is used.
-
-     Expire-Date: <iso-date>
-        Set the notBefore date of the certificate.  Either a date like
-        "1986-04-26" or a full timestamp like "19860426T042640" may be
-        used.  The time is considered to be UTC.  If it is not given a
-        default value is used.
-
-     Signing-Key: <keygrip>
-        This gives the keygrip of the key used to sign the
-        certificate.  If it is not given a self-signed certificate
-        will be created.
-
-     Hash-Algo: <hash-algo>
-        Use HASH-ALGO for this certificate.  The supported hash
-        algorithms are: "sha-1", "sha-256", "sha-384" and "sha-512".
-        "sha-1" is the default.
-
-Here is an example:
-$ cat >foo <<EOF
-%echo Generating a standard key
-Key-Type: RSA
-Key-Length: 2048
-Name-DN: CN=test cert 1,OU=Aegypten Project,O=g10 Code GmbH,L=Düsseldorf,C=DE
-Name-Email: joe@foo.bar
-# Do a commit here, so that we can later print "done" :-)
-%commit
-%echo done
-EOF
+   The format of the parameter file is described in the manual under
+   "Unattended Usage".
+
+   Here is an example:
+     $ cat >foo <<EOF
+     %echo Generating a standard key
+     Key-Type: RSA
+     Key-Length: 2048
+     Name-DN: CN=test cert 1,OU=Aegypten Project,O=g10 Code GmbH,L=Ddorf,C=DE
+     Name-Email: joe@foo.bar
+     # Do a commit here, so that we can later print a "done"
+     %commit
+     %echo done
+     EOF
+
+   This parameter file was used to create the STEED CA:
+     Key-Type: RSA
+     Key-Length: 1024
+     Key-Grip: 68A638998DFABAC510EA645CE34F9686B2EDF7EA
+     Key-Usage: cert
+     Serial: 1
+     Name-DN: CN=The STEED Self-Signing Nonthority
+     Not-Before: 2011-11-11
+     Not-After: 2106-02-06
+     Subject-Key-Id: 68A638998DFABAC510EA645CE34F9686B2EDF7EA
+     Extension: 2.5.29.19 c 30060101ff020101
+     Extension: 1.3.6.1.4.1.11591.2.2.2 n 0101ff
+     Signing-Key: 68A638998DFABAC510EA645CE34F9686B2EDF7EA
+     %commit
+
 */
 
 
@@ -175,7 +84,10 @@ enum para_name
     pNOTBEFORE,
     pNOTAFTER,
     pSIGNINGKEY,
-    pHASHALGO
+    pHASHALGO,
+    pAUTHKEYID,
+    pSUBJKEYID,
+    pEXTENSION
   };
 
 struct para_data_s
@@ -196,6 +108,8 @@ struct reqgen_ctrl_s
 };
 
 
+static const char oidstr_authorityKeyIdentifier[] = "2.5.29.35";
+static const char oidstr_subjectKeyIdentifier[] = "2.5.29.14";
 static const char oidstr_keyUsage[] = "2.5.29.15";
 static const char oidstr_basicConstraints[] = "2.5.29.19";
 static const char oidstr_standaloneCertificate[] = "1.3.6.1.4.1.11591.2.2.1";
@@ -277,8 +191,11 @@ parse_parameter_usage (struct para_data_s *para, enum para_name key)
         ;
       else if ( !ascii_strcasecmp (p, "sign") )
         use |= GCRY_PK_USAGE_SIGN;
-      else if ( !ascii_strcasecmp (p, "encrypt") )
+      else if ( !ascii_strcasecmp (p, "encrypt")
+                || !ascii_strcasecmp (p, "encr") )
         use |= GCRY_PK_USAGE_ENCR;
+      else if ( !ascii_strcasecmp (p, "cert") )
+        use |= GCRY_PK_USAGE_CERT;
       else
         {
           log_error ("line %d: invalid usage list\n", r->lnr);
@@ -332,6 +249,9 @@ read_parameters (ctrl_t ctrl, estream_t fp, estream_t out_fp)
     { "Not-After",      pNOTAFTER },
     { "Signing-Key",    pSIGNINGKEY },
     { "Hash-Algo",      pHASHALGO },
+    { "Authority-Key-Id", pAUTHKEYID },
+    { "Subject-Key-Id", pSUBJKEYID },
+    { "Extension",      pEXTENSION, 1 },
     { NULL, 0 }
   };
   char line[1024], *p;
@@ -516,7 +436,7 @@ proc_parameters (ctrl_t ctrl, struct para_data_s *para,
   unsigned int nbits;
   char numbuf[20];
   unsigned char keyparms[100];
-  int rc;
+  int rc = 0;
   ksba_sexp_t public;
   ksba_sexp_t sigkey = NULL;
   int seq;
@@ -701,6 +621,74 @@ proc_parameters (ctrl_t ctrl, struct para_data_s *para,
       }
   }
 
+  /* Check the optional AuthorityKeyId.  */
+  string = get_parameter_value (para, pAUTHKEYID, 0);
+  if (string)
+    {
+      for (s=string, i=0; hexdigitp (s); s++, i++)
+        ;
+      if (*s || (i&1))
+        {
+          r = get_parameter (para, pAUTHKEYID, 0);
+          log_error (_("line %d: invalid authority-key-id\n"), r->lnr);
+          xfree (cardkeyid);
+          return gpg_error (GPG_ERR_INV_PARAMETER);
+        }
+    }
+
+  /* Check the optional SubjectKeyId.  */
+  string = get_parameter_value (para, pSUBJKEYID, 0);
+  if (string)
+    {
+      for (s=string, i=0; hexdigitp (s); s++, i++)
+        ;
+      if (*s || (i&1))
+        {
+          r = get_parameter (para, pSUBJKEYID, 0);
+          log_error (_("line %d: invalid subject-key-id\n"), r->lnr);
+          xfree (cardkeyid);
+          return gpg_error (GPG_ERR_INV_PARAMETER);
+        }
+    }
+
+  /* Check the optional extensions. */
+  for (seq=0; (string=get_parameter_value (para, pEXTENSION, seq)); seq++)
+    {
+      int okay = 0;
+
+      s = strpbrk (string, " \t:");
+      if (s)
+        {
+          s++;
+          while (spacep (s))
+            s++;
+          if (*s && strchr ("nNcC", *s))
+            {
+              s++;
+              while (spacep (s))
+                s++;
+              if (*s == ':')
+                s++;
+              if (*s)
+                {
+                  while (spacep (s))
+                    s++;
+                  for (i=0; hexdigitp (s); s++, i++)
+                    ;
+                  if (!((*s && *s != ':') || !i || (i&1)))
+                    okay = 1;
+                }
+            }
+        }
+      if (!okay)
+        {
+          r = get_parameter (para, pEXTENSION, seq);
+          log_error (_("line %d: invalid extension syntax\n"), r->lnr);
+          xfree (cardkeyid);
+          return gpg_error (GPG_ERR_INV_PARAMETER);
+        }
+    }
+
   /* Create or retrieve the public key.  */
   if (cardkeyid) /* Take the key from the current smart card. */
     {
@@ -827,7 +815,7 @@ create_request (ctrl_t ctrl,
       goto leave;
     }
   if (DBG_HASHING)
-    gcry_md_start_debug (md, "cr.cri");
+    gcry_md_debug (md, "cr.cri");
 
   ksba_certreq_set_hash_function (cr, HASH_FNC, md);
   ksba_certreq_set_writer (cr, writer);
@@ -945,6 +933,14 @@ create_request (ctrl_t ctrl,
       err = ksba_certreq_add_extension (cr, oidstr_keyUsage, 1,
                                         "\x03\x02\x04\x30", 4);
     }
+  else if (use == GCRY_PK_USAGE_CERT)
+    {
+      /* For certify only we encode the bits:
+         KSBA_KEYUSAGE_KEY_CERT_SIGN
+         KSBA_KEYUSAGE_CRL_SIGN */
+      err = ksba_certreq_add_extension (cr, oidstr_keyUsage, 1,
+                                        "\x03\x02\x01\x06", 4);
+    }
   else
     err = 0; /* Both or none given: don't request one. */
   if (err)
@@ -996,7 +992,7 @@ create_request (ctrl_t ctrl,
           *p++ = '0';
           strcpy (p, string);
           for (p=hexbuf, len=0; p[0] && p[1]; p += 2)
-            ((unsigned char*)hexbuf)[len++] = xtoi_2 (s);
+            ((unsigned char*)hexbuf)[len++] = xtoi_2 (p);
           /* Now build the S-expression.  */
           snprintf (numbuf, DIM(numbuf), "%u:", (unsigned int)len);
           buf = p = xtrymalloc (1 + strlen (numbuf) + len + 1 + 1);
@@ -1116,6 +1112,139 @@ create_request (ctrl_t ctrl,
             goto leave;
           }
       }
+
+      /* Insert the AuthorityKeyId.  */
+      string = get_parameter_value (para, pAUTHKEYID, 0);
+      if (string)
+        {
+          char *hexbuf;
+
+          /* Allocate a buffer for in-place conversion.  We also add 4
+             extra bytes space for the tags and lengths fields.  */
+          hexbuf = xtrymalloc (4 + strlen (string) + 1);
+          if (!hexbuf)
+            {
+              err = gpg_error_from_syserror ();
+              goto leave;
+            }
+          strcpy (hexbuf+4, string);
+          for (p=hexbuf+4, len=0; p[0] && p[1]; p += 2)
+            ((unsigned char*)hexbuf)[4+len++] = xtoi_2 (p);
+          if (len > 125)
+            {
+              err = gpg_error (GPG_ERR_TOO_LARGE);
+              xfree (hexbuf);
+              goto leave;
+            }
+          hexbuf[0] = 0x30;  /* Tag for a Sequence.  */
+          hexbuf[1] = len+2;
+          hexbuf[2] = 0x80;  /* Context tag for an implicit Octet string.  */
+          hexbuf[3] = len;
+          err = ksba_certreq_add_extension (cr, oidstr_authorityKeyIdentifier,
+                                            0,
+                                            hexbuf, 4+len);
+          xfree (hexbuf);
+          if (err)
+            {
+              log_error ("error setting the authority-key-id: %s\n",
+                         gpg_strerror (err));
+              goto leave;
+            }
+        }
+
+      /* Insert the SubjectKeyId.  */
+      string = get_parameter_value (para, pSUBJKEYID, 0);
+      if (string)
+        {
+          char *hexbuf;
+
+          /* Allocate a buffer for in-place conversion.  We also add 2
+             extra bytes space for the tag and length field.  */
+          hexbuf = xtrymalloc (2 + strlen (string) + 1);
+          if (!hexbuf)
+            {
+              err = gpg_error_from_syserror ();
+              goto leave;
+            }
+          strcpy (hexbuf+2, string);
+          for (p=hexbuf+2, len=0; p[0] && p[1]; p += 2)
+            ((unsigned char*)hexbuf)[2+len++] = xtoi_2 (p);
+          if (len > 127)
+            {
+              err = gpg_error (GPG_ERR_TOO_LARGE);
+              xfree (hexbuf);
+              goto leave;
+            }
+          hexbuf[0] = 0x04;  /* Tag for an Octet string.  */
+          hexbuf[1] = len;
+          err = ksba_certreq_add_extension (cr, oidstr_subjectKeyIdentifier, 0,
+                                            hexbuf, 2+len);
+          xfree (hexbuf);
+          if (err)
+            {
+              log_error ("error setting the subject-key-id: %s\n",
+                         gpg_strerror (err));
+              goto leave;
+            }
+        }
+
+      /* Insert additional extensions.  */
+      for (seq=0; (string = get_parameter_value (para, pEXTENSION, seq)); seq++)
+        {
+          char *hexbuf;
+          char *oidstr;
+          int crit = 0;
+
+          s = strpbrk (string, " \t:");
+          if (!s)
+            {
+              err = gpg_error (GPG_ERR_INTERNAL);
+              goto leave;
+            }
+
+          oidstr = xtrymalloc (s - string + 1);
+          if (!oidstr)
+            {
+              err = gpg_error_from_syserror ();
+              goto leave;
+            }
+          memcpy (oidstr, string, (s-string));
+          oidstr[(s-string)] = 0;
+
+          s++;
+          while (spacep (s))
+            s++;
+          if (!*s)
+            {
+              err = gpg_error (GPG_ERR_INTERNAL);
+              xfree (oidstr);
+              goto leave;
+            }
+
+          if (strchr ("cC", *s))
+            crit = 1;
+          s++;
+          while (spacep (s))
+            s++;
+          if (*s == ':')
+            s++;
+          while (spacep (s))
+            s++;
+
+          hexbuf = xtrystrdup (s);
+          if (!hexbuf)
+            {
+              err = gpg_error_from_syserror ();
+              xfree (oidstr);
+              goto leave;
+            }
+          for (p=hexbuf, len=0; p[0] && p[1]; p += 2)
+            ((unsigned char*)hexbuf)[len++] = xtoi_2 (p);
+          err = ksba_certreq_add_extension (cr, oidstr, crit,
+                                            hexbuf, len);
+          xfree (oidstr);
+          xfree (hexbuf);
+        }
     }
   else
     sigkey = public;