Commit message: sm: Avoid double-free on iconv failure

* sm/minip12.c: (p12_build) if jnlib_iconv_open fails, avoid
double-free of pwbuf.

--

Observed by Joshua Rogers <honey@internot.info>, who proposed a
slightly different fix.

Debian-Bug-Id: 773472

Added fix at a second place - wk.
CVE: None
Vulnerability description: None

Project: gnupg
File: minip12.c

set_key_iv_pbes2 (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter,
                  const void *iv, size_t ivlen, const char *pw, int algo)
{
  unsigned char *keybuf;
  size_t keylen;
  int rc;

  keylen = gcry_cipher_get_algo_keylen (algo);
  if (!keylen)
    return -1;
  keybuf = gcry_malloc_secure (keylen);
  if (!keybuf)
    return -1;

  rc = gcry_kdf_derive (pw, strlen (pw),
                        GCRY_KDF_PBKDF2, GCRY_MD_SHA1,
                        salt, saltlen, iter, keylen, keybuf);
  if (rc)
    {
      log_error ("gcry_kdf_derive failed: %s\n", gpg_strerror (rc));
      gcry_free (keybuf);
      return -1;
    }

  rc = gcry_cipher_setkey (chd, keybuf, keylen);
  gcry_free (keybuf);
  if (rc)
    {
      log_error ("gcry_cipher_setkey failed: %s\n", gpg_strerror (rc));
      return -1;
    }


  rc = gcry_cipher_setiv (chd, iv, ivlen);
  if (rc)
    {
      log_error ("gcry_cipher_setiv failed: %s\n", gpg_strerror (rc));
      return -1;
    }
  return 0;
}

Project: gnupg
File: minip12.c

main (int argc, char **argv)
{
  FILE *fp;
  struct stat st;
  unsigned char *buf;
  size_t buflen;
  gcry_mpi_t *result;
  int badpass;

  if (argc != 3)
    {
      fprintf (stderr, "usage: testp12 file passphrase\n");
      return 1;
    }

  gcry_control (GCRYCTL_DISABLE_SECMEM, NULL);
  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, NULL);

  fp = fopen (argv[1], "rb");
  if (!fp)
    {
      fprintf (stderr, "can't open '%s': %s\n", argv[1], strerror (errno));
      return 1;
    }

  if (fstat (fileno(fp), &st))
    {
      fprintf (stderr, "can't stat '%s': %s\n", argv[1], strerror (errno));
      return 1;
    }

  buflen = st.st_size;
  buf = gcry_malloc (buflen+1);
  if (!buf || fread (buf, buflen, 1, fp) != 1)
    {
      fprintf (stderr, "error reading '%s': %s\n", argv[1], strerror (errno));
      return 1;
    }
  fclose (fp);

  result = p12_parse (buf, buflen, argv[2], cert_cb, NULL, &badpass);
  if (result)
    {
      int i, rc;
      unsigned char *tmpbuf;

      for (i=0; result[i]; i++)
        {
          rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &tmpbuf,
                                NULL, result[i]);
          if (rc)
            printf ("%d: [error printing number: %s]\n",
                    i, gpg_strerror (rc));
          else
            {
              printf ("%d: %s\n", i, tmpbuf);
              gcry_free (tmpbuf);
            }
        }
    }

  return 0;

}

Project: gnupg
File: minip12.c

cram_octet_string (const unsigned char *input, size_t *length,
                   size_t *input_consumed)
{
  const unsigned char *s = input;
  size_t n = *length;
  unsigned char *output, *d;
  struct tag_info ti;

  /* Allocate output buf.  We know that it won't be longer than the
     input buffer. */
  d = output = gcry_malloc (n);
  if (!output)
    goto bailout;

  for (;;)
    {
      if (parse_tag (&s, &n, &ti))
        goto bailout;
      if (ti.class == UNIVERSAL && ti.tag == TAG_OCTET_STRING
          && !ti.ndef && !ti.is_constructed)
        {
          memcpy (d, s, ti.length);
          s += ti.length;
          d += ti.length;
          n -= ti.length;
        }
      else if (ti.class == UNIVERSAL && !ti.tag && !ti.is_constructed)
        break; /* Ready */
      else
        goto bailout;
    }


  *length = d - output;
  if (input_consumed)
    *input_consumed += s - input;
  return output;

 bailout:
  if (input_consumed)
    *input_consumed += s - input;
  gcry_free (output);
  return NULL;
}

Project: gnupg
File: minip12.c

crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen,
             int iter, const void *iv, size_t ivlen,
             const char *pw, int cipher_algo, int encrypt)
{
  gcry_cipher_hd_t chd;
  int rc;

  rc = gcry_cipher_open (&chd, cipher_algo, GCRY_CIPHER_MODE_CBC, 0);
  if (rc)
    {
      log_error ( "gcry_cipher_open failed: %s\n", gpg_strerror(rc));
      wipememory (buffer, length);
      return;
    }

  if (cipher_algo == GCRY_CIPHER_AES128
      ? set_key_iv_pbes2 (chd, salt, saltlen, iter, iv, ivlen, pw, cipher_algo)
      : set_key_iv (chd, salt, saltlen, iter, pw,
                    cipher_algo == GCRY_CIPHER_RFC2268_40? 5:24))
    {
      wipememory (buffer, length);
      goto leave;
    }

  rc = encrypt? gcry_cipher_encrypt (chd, buffer, length, NULL, 0)
              : gcry_cipher_decrypt (chd, buffer, length, NULL, 0);

  if (rc)
    {
      wipememory (buffer, length);
      log_error ( "en/de-crytion failed: %s\n", gpg_strerror (rc));
      goto leave;
    }

 leave:
  gcry_cipher_close (chd);
}

Project: gnupg
File: minip12.c

parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti)
{
  int c;
  unsigned long tag;
  const unsigned char *buf = *buffer;
  size_t length = *size;

  ti->length = 0;
  ti->ndef = 0;
  ti->nhdr = 0;

  /* Get the tag */
  if (!length)
    return -1; /* premature eof */
  c = *buf++; length--;
  ti->nhdr++;

  ti->class = (c & 0xc0) >> 6;
  ti->is_constructed = !!(c & 0x20);
  tag = c & 0x1f;

  if (tag == 0x1f)
    {
      tag = 0;
      do
        {
          tag <<= 7;
          if (!length)
            return -1; /* premature eof */
          c = *buf++; length--;
          ti->nhdr++;
          tag |= c & 0x7f;
        }
      while (c & 0x80);
    }
  ti->tag = tag;

  /* Get the length */
  if (!length)
    return -1; /* prematureeof */
  c = *buf++; length--;
  ti->nhdr++;

  if ( !(c & 0x80) )
    ti->length = c;
  else if (c == 0x80)
    ti->ndef = 1;
  else if (c == 0xff)
    return -1; /* forbidden length value */
  else
    {
      unsigned long len = 0;
      int count = c & 0x7f;

      for (; count; count--)
        {
          len <<= 8;
          if (!length)
            return -1; /* premature_eof */
          c = *buf++; length--;
          ti->nhdr++;
          len |= c & 0xff;
        }
      ti->length = len;
    }

  if (ti->class == UNIVERSAL && !ti->tag)
    ti->length = 0;

  if (ti->length > length)
    return -1; /* data larger than buffer. */

  *buffer = buf;
  *size = length;
  return 0;
}

Project: gnupg
File: minip12.c

build_key_sequence (gcry_mpi_t *kparms, int mode, size_t *r_length)
{
  int rc, i;
  size_t needed, n;
  unsigned char *plain, *p;
  size_t plainlen;
  size_t outseqlen, oidseqlen, octstrlen, inseqlen;

  needed = 3; /* The version integer with value 0. */
  for (i=0; kparms[i]; i++)
    {
      n = 0;
      rc = gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &n, kparms[i]);
      if (rc)
        {
          log_error ("error formatting parameter: %s\n", gpg_strerror (rc));
          return NULL;
        }
      needed += n;
      n = compute_tag_length (n);
      if (!n)
        return NULL;
      needed += n;
    }
  if (i != 8)
    {
      log_error ("invalid parameters for p12_build\n");
      return NULL;
    }
  /* Now this all goes into a sequence. */
  inseqlen = needed;
  n = compute_tag_length (needed);
  if (!n)
    return NULL;
  needed += n;

  if (mode != 2)
    {
      /* Encapsulate all into an octet string. */
      octstrlen = needed;
      n = compute_tag_length (needed);
      if (!n)
        return NULL;
      needed += n;
      /* Prepend the object identifier sequence. */
      oidseqlen = 2 + DIM (oid_rsaEncryption) + 2;
      needed += 2 + oidseqlen;
      /* The version number. */
      needed += 3;
      /* And finally put the whole thing into a sequence. */
      outseqlen = needed;
      n = compute_tag_length (needed);
      if (!n)
        return NULL;
      needed += n;
    }

  /* allocate 8 extra bytes for padding */
  plain = gcry_malloc_secure (needed+8);
  if (!plain)
    {
      log_error ("error allocating encryption buffer\n");
      return NULL;
    }

  /* And now fill the plaintext buffer. */
  p = plain;
  if (mode != 2)
    {
      p = store_tag_length (p, TAG_SEQUENCE, outseqlen);
      /* Store version. */
      *p++ = TAG_INTEGER;
      *p++ = 1;
      *p++ = 0;
      /* Store object identifier sequence. */
      p = store_tag_length (p, TAG_SEQUENCE, oidseqlen);
      p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_rsaEncryption));
      memcpy (p, oid_rsaEncryption, DIM (oid_rsaEncryption));
      p += DIM (oid_rsaEncryption);
      *p++ = TAG_NULL;
      *p++ = 0;
      /* Start with the octet string. */
      p = store_tag_length (p, TAG_OCTET_STRING, octstrlen);
    }

  p = store_tag_length (p, TAG_SEQUENCE, inseqlen);
  /* Store the key parameters. */
  *p++ = TAG_INTEGER;
  *p++ = 1;
  *p++ = 0;
  for (i=0; kparms[i]; i++)
    {
      n = 0;
      rc = gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &n, kparms[i]);
      if (rc)
        {
          log_error ("oops: error formatting parameter: %s\n",
                     gpg_strerror (rc));
          gcry_free (plain);
          return NULL;
        }
      p = store_tag_length (p, TAG_INTEGER, n);

      n = plain + needed - p;
      rc = gcry_mpi_print (GCRYMPI_FMT_STD, p, n, &n, kparms[i]);
      if (rc)
        {
          log_error ("oops: error storing parameter: %s\n",
                     gpg_strerror (rc));
          gcry_free (plain);
          return NULL;
        }
      p += n;
    }

  plainlen = p - plain;
  assert (needed == plainlen);

  if (!mode)
    {
      /* Append some pad characters; we already allocated extra space. */
      n = 8 - plainlen % 8;
      for (i=0; i < n; i++, plainlen++)
        *p++ = n;
    }

  *r_length = plainlen;
  return plain;
}

Project: gnupg
File: minip12.c

bag_decrypted_data_p (const void *plaintext, size_t length)
{
  struct tag_info ti;
  const unsigned char *p = plaintext;
  size_t n = length;

  /*   { */
  /* #  warning debug code is enabled */
  /*     FILE *fp = fopen ("tmp-rc2-plain.der", "wb"); */
  /*     if (!fp || fwrite (p, n, 1, fp) != 1) */
  /*       exit (2); */
  /*     fclose (fp); */
  /*   } */

  if (parse_tag (&p, &n, &ti))
    return 0;
  if (ti.class || ti.tag != TAG_SEQUENCE)
    return 0;
  if (parse_tag (&p, &n, &ti))
    return 0;

  return 1;
}

Project: gnupg
File: minip12.c

build_cert_bag (unsigned char *buffer, size_t buflen, char *salt,
                size_t *r_length)
{
  size_t len[9], needed;
  unsigned char *p, *certbag;
  size_t certbaglen;

  /* Walk 9 steps down to collect the info: */

  /* 8. The data goes into an octet string. */
  needed = compute_tag_length (buflen);
  needed += buflen;

  /* 7. The algorithm identifier. */
  needed += DIM (data_rc2iter2048);

  /* 6. The data OID. */
  needed += 2 + DIM (oid_data);

  /* 5. A sequence. */
  len[5] = needed;
  needed += compute_tag_length ( needed);

  /* 4. An integer. */
  needed += 3;

  /* 3. A sequence. */
  len[3] = needed;
  needed += compute_tag_length (needed);

  /* 2.  A [0] tag. */
  len[2] = needed;
  needed += compute_tag_length (needed);

  /* 1. The encryptedData OID. */
  needed += 2 + DIM (oid_encryptedData);

  /* 0. The first sequence. */
  len[0] = needed;
  needed += compute_tag_length (needed);

  /* Now that we have all length information, allocate a buffer. */
  p = certbag = gcry_malloc (needed);
  if (!certbag)
    {
      log_error ("error allocating buffer\n");
      return NULL;
    }

  /* Walk 9 steps up to store the data. */

  /* 0. Store the first sequence. */
  p = store_tag_length (p, TAG_SEQUENCE, len[0]);

  /* 1. Store the encryptedData OID. */
  p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_encryptedData));
  memcpy (p, oid_encryptedData, DIM (oid_encryptedData));
  p += DIM (oid_encryptedData);

  /* 2. Store a [0] tag. */
  p = store_tag_length (p, 0xa0, len[2]);

  /* 3. Store a sequence. */
  p = store_tag_length (p, TAG_SEQUENCE, len[3]);

  /* 4. Store the integer 0. */
  *p++ = TAG_INTEGER;
  *p++ = 1;
  *p++ = 0;

  /* 5. Store a sequence. */
  p = store_tag_length (p, TAG_SEQUENCE, len[5]);

  /* 6. Store the data OID. */
  p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data));
  memcpy (p, oid_data, DIM (oid_data));
  p += DIM (oid_data);

  /* 7. Now for the pre-encoded algorithm identifier and the salt. */
  memcpy (p, data_rc2iter2048, DIM (data_rc2iter2048));
  memcpy (p + DATA_RC2ITER2048_SALT_OFF, salt, 8);
  p += DIM (data_rc2iter2048);

  /* 8. And finally the [0] tag with the encrypted data. */
  p = store_tag_length (p, 0x80, buflen);
  memcpy (p, buffer, buflen);
  p += buflen;
  certbaglen = p - certbag;

  if (needed != certbaglen)
    log_debug ("length mismatch: %lu, %lu\n",
               (unsigned long)needed, (unsigned long)certbaglen);

  *r_length = certbaglen;
  return certbag;
}

Project: gnupg
File: minip12.c

compute_tag_length (size_t n)
{
  int needed = 0;

  if (n < 128)
    needed += 2; /* tag and one length byte */
  else if (n < 256)
    needed += 3; /* tag, number of length bytes, 1 length byte */
  else if (n < 65536)
    needed += 4; /* tag, number of length bytes, 2 length bytes */
  else
    {
      log_error ("object too larger to encode\n");
      return 0;
    }
  return needed;
}

Project: gnupg
File: minip12.c

decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length,
               char *salt, size_t saltlen,
               int iter, const void *iv, size_t ivlen,
               const char *pw, int cipher_algo,
               int (*check_fnc) (const void *, size_t))
{
  static const char * const charsets[] = {
    "",   /* No conversion - use the UTF-8 passphrase direct.  */
    "ISO-8859-1",
    "ISO-8859-15",
    "ISO-8859-2",
    "ISO-8859-3",
    "ISO-8859-4",
    "ISO-8859-5",
    "ISO-8859-6",
    "ISO-8859-7",
    "ISO-8859-8",
    "ISO-8859-9",
    "KOI8-R",
    "IBM437",
    "IBM850",
    "EUC-JP",
    "BIG5",
    NULL
  };
  int charsetidx = 0;
  char *convertedpw = NULL;   /* Malloced and converted password or NULL.  */
  size_t convertedpwsize = 0; /* Allocated length.  */

  for (charsetidx=0; charsets[charsetidx]; charsetidx++)
    {
      if (*charsets[charsetidx])
        {
          jnlib_iconv_t cd;
          const char *inptr;
          char *outptr;
          size_t inbytes, outbytes;

          if (!convertedpw)
            {
              /* We assume one byte encodings.  Thus we can allocate
                 the buffer of the same size as the original
                 passphrase; the result will actually be shorter
                 then.  */
              convertedpwsize = strlen (pw) + 1;
              convertedpw = gcry_malloc_secure (convertedpwsize);
              if (!convertedpw)
                {
                  log_info ("out of secure memory while"
                            " converting passphrase\n");
                  break; /* Give up.  */
                }
            }

          cd = jnlib_iconv_open (charsets[charsetidx], "utf-8");
          if (cd == (jnlib_iconv_t)(-1))
            continue;

          inptr = pw;
          inbytes = strlen (pw);
          outptr = convertedpw;
          outbytes = convertedpwsize - 1;
          if ( jnlib_iconv (cd, (const char **)&inptr, &inbytes,
                      &outptr, &outbytes) == (size_t)-1)
            {
              jnlib_iconv_close (cd);
              continue;
            }
          *outptr = 0;
          jnlib_iconv_close (cd);
          log_info ("decryption failed; trying charset '%s'\n",
                    charsets[charsetidx]);
        }
      memcpy (plaintext, ciphertext, length);
      crypt_block (plaintext, length, salt, saltlen, iter, iv, ivlen,
                   convertedpw? convertedpw:pw, cipher_algo, 0);
      if (check_fnc (plaintext, length))
        break; /* Decryption succeeded. */
    }
  gcry_free (convertedpw);
}

Project: gnupg
File: minip12.c

set_key_iv (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter,
            const char *pw, int keybytes)
{
  unsigned char keybuf[24];
  int rc;

  assert (keybytes == 5 || keybytes == 24);
  if (string_to_key (1, salt, saltlen, iter, pw, keybytes, keybuf))
    return -1;
  rc = gcry_cipher_setkey (chd, keybuf, keybytes);
  if (rc)
    {
      log_error ( "gcry_cipher_setkey failed: %s\n", gpg_strerror (rc));
      return -1;
    }

  if (string_to_key (2, salt, saltlen, iter, pw, 8, keybuf))
    return -1;
  rc = gcry_cipher_setiv (chd, keybuf, 8);
  if (rc)
    {
      log_error ("gcry_cipher_setiv failed: %s\n", gpg_strerror (rc));
      return -1;
    }
  return 0;
}

Project: gnupg
File: minip12.c

build_cert_sequence (const unsigned char *buffer, size_t buflen,
                     const unsigned char *sha1hash, const char *keyidstr,
                     size_t *r_length)
{
  size_t len[8], needed, n;
  unsigned char *p, *certseq;
  size_t certseqlen;
  int i;

  assert (strlen (keyidstr) == 8);

  /* Walk 8 steps down to collect the info: */

  /* 7. The data goes into an octet string. */
  needed = compute_tag_length (buflen);
  needed += buflen;

  /* 6. A [0] tag. */
  len[6] = needed;
  needed += compute_tag_length (needed);

  /* 5. An OID. */
  needed += 2 + DIM (oid_x509Certificate_for_pkcs_12);

  /* 4. A sequence. */
  len[4] = needed;
  needed += compute_tag_length (needed);

  /* 3. A [0] tag. */
  len[3] = needed;
  needed += compute_tag_length (needed);

  /* 2b. The attributes which are appended at the end. */
  if (sha1hash)
    needed += DIM (data_attrtemplate) + 20;

  /* 2. An OID. */
  needed += 2 + DIM (oid_pkcs_12_CertBag);

  /* 1. A sequence. */
  len[1] = needed;
  needed += compute_tag_length (needed);

  /* 0. The first sequence. */
  len[0] = needed;
  needed += compute_tag_length (needed);

  /* Now that we have all length information, allocate a buffer. */
  p = certseq = gcry_malloc (needed + 8 /*(for padding)*/);
  if (!certseq)
    {
      log_error ("error allocating buffer\n");
      return NULL;
    }

  /* Walk 8 steps up to store the data. */

  /* 0. Store the first sequence. */
  p = store_tag_length (p, TAG_SEQUENCE, len[0]);

  /* 1. Store the second sequence. */
  p = store_tag_length (p, TAG_SEQUENCE, len[1]);

  /* 2. Store the pkcs12-cert-bag OID. */
  p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_pkcs_12_CertBag));
  memcpy (p, oid_pkcs_12_CertBag, DIM (oid_pkcs_12_CertBag));
  p += DIM (oid_pkcs_12_CertBag);

  /* 3. Store a [0] tag. */
  p = store_tag_length (p, 0xa0, len[3]);

  /* 4. Store a sequence. */
  p = store_tag_length (p, TAG_SEQUENCE, len[4]);

  /* 5. Store the x509Certificate OID. */
  p = store_tag_length (p, TAG_OBJECT_ID,
                        DIM (oid_x509Certificate_for_pkcs_12));
  memcpy (p, oid_x509Certificate_for_pkcs_12,
          DIM (oid_x509Certificate_for_pkcs_12));
  p += DIM (oid_x509Certificate_for_pkcs_12);

  /* 6. Store a [0] tag. */
  p = store_tag_length (p, 0xa0, len[6]);

  /* 7. And the octet string with the actual certificate. */
  p = store_tag_length (p, TAG_OCTET_STRING, buflen);
  memcpy (p, buffer, buflen);
  p += buflen;

  /* Append the attributes whose length we calculated at step 2b. */
  if (sha1hash)
    {
      memcpy (p, data_attrtemplate, DIM (data_attrtemplate));
      for (i=0; i < 8; i++)
        p[DATA_ATTRTEMPLATE_KEYID_OFF+2*i+1] = keyidstr[i];
      p += DIM (data_attrtemplate);
      memcpy (p, sha1hash, 20);
      p += 20;
    }

  certseqlen = p - certseq;
  if (needed != certseqlen)
    log_debug ("length mismatch: %lu, %lu\n",
               (unsigned long)needed, (unsigned long)certseqlen);

  /* Append some pad characters; we already allocated extra space. */
  n = 8 - certseqlen % 8;
  for (i=0; i < n; i++, certseqlen++)
    *p++ = n;

  *r_length = certseqlen;
  return certseq;
}

Project: gnupg
File: minip12.c

bag_data_p (const void *plaintext, size_t length)
{
  struct tag_info ti;
  const unsigned char *p = plaintext;
  size_t n = length;

/*   { */
/* #  warning debug code is enabled */
/*     FILE *fp = fopen ("tmp-3des-plain-key.der", "wb"); */
/*     if (!fp || fwrite (p, n, 1, fp) != 1) */
/*       exit (2); */
/*     fclose (fp); */
/*   } */

  if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
    return 0;
  if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER
      || ti.length != 1 || *p)
    return 0;

  return 1;
}

Project: gnupg
File: minip12.c

p12_parse (const unsigned char *buffer, size_t length, const char *pw,
           void (*certcb)(void*, const unsigned char*, size_t),
           void *certcbarg, int *r_badpass)
{
  struct tag_info ti;
  const unsigned char *p = buffer;
  const unsigned char *p_start = buffer;
  size_t n = length;
  const char *where;
  int bagseqlength, len;
  int bagseqndef, lenndef;
  gcry_mpi_t *result = NULL;
  unsigned char *cram_buffer = NULL;

  *r_badpass = 0;
  where = "pfx";
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.tag != TAG_SEQUENCE)
    goto bailout;

  where = "pfxVersion";
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.tag != TAG_INTEGER || ti.length != 1 || *p != 3)
    goto bailout;
  p++; n--;

  where = "authSave";
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.tag != TAG_SEQUENCE)
    goto bailout;
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_data)
      || memcmp (p, oid_data, DIM(oid_data)))
    goto bailout;
  p += DIM(oid_data);
  n -= DIM(oid_data);

  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.class != ASNCONTEXT || ti.tag)
    goto bailout;
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.class != UNIVERSAL || ti.tag != TAG_OCTET_STRING)
    goto bailout;

  if (ti.is_constructed && ti.ndef)
    {
      /* Mozilla exported certs now come with single byte chunks of
         octect strings.  (Mozilla Firefox 1.0.4).  Arghh. */
      where = "cram-bags";
      cram_buffer = cram_octet_string ( p, &n, NULL);
      if (!cram_buffer)
        goto bailout;
      p = p_start = cram_buffer;
    }

  where = "bags";
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.class != UNIVERSAL || ti.tag != TAG_SEQUENCE)
    goto bailout;
  bagseqndef = ti.ndef;
  bagseqlength = ti.length;
  while (bagseqlength || bagseqndef)
    {
/*       log_debug ( "at offset %u\n", (p - p_start)); */
      where = "bag-sequence";
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (bagseqndef && ti.class == UNIVERSAL && !ti.tag && !ti.is_constructed)
        break; /* Ready */
      if (ti.class != UNIVERSAL || ti.tag != TAG_SEQUENCE)
        goto bailout;

      if (!bagseqndef)
        {
          if (bagseqlength < ti.nhdr)
            goto bailout;
          bagseqlength -= ti.nhdr;
          if (bagseqlength < ti.length)
            goto bailout;
          bagseqlength -= ti.length;
        }
      lenndef = ti.ndef;
      len = ti.length;

      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (lenndef)
        len = ti.nhdr;
      else
        len -= ti.nhdr;

      if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_encryptedData)
          && !memcmp (p, oid_encryptedData, DIM(oid_encryptedData)))
        {
          size_t consumed = 0;

          p += DIM(oid_encryptedData);
          n -= DIM(oid_encryptedData);
          if (!lenndef)
            len -= DIM(oid_encryptedData);
          where = "bag.encryptedData";
          if (parse_bag_encrypted_data (p, n, (p - p_start), &consumed, pw,
                                        certcb, certcbarg,
                                        result? NULL : &result, r_badpass))
            goto bailout;
          if (lenndef)
            len += consumed;
        }
      else if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_data)
               && !memcmp (p, oid_data, DIM(oid_data)))
        {
          if (result)
            {
              log_info ("already got an key object, skipping this one\n");
              p += ti.length;
              n -= ti.length;
            }
          else
            {
              size_t consumed = 0;

              p += DIM(oid_data);
              n -= DIM(oid_data);
              if (!lenndef)
                len -= DIM(oid_data);
              result = parse_bag_data (p, n, (p - p_start), &consumed, pw);
              if (!result)
                goto bailout;
              if (lenndef)
                len += consumed;
            }
        }
      else
        {
          log_info ("unknown bag type - skipped\n");
          p += ti.length;
          n -= ti.length;
        }

      if (len < 0 || len > n)
        goto bailout;
      p += len;
      n -= len;
      if (lenndef)
        {
          /* Need to skip the Null Tag. */
          if (parse_tag (&p, &n, &ti))
            goto bailout;
          if (!(ti.class == UNIVERSAL && !ti.tag && !ti.is_constructed))
            goto bailout;
        }
    }

  gcry_free (cram_buffer);
  return result;
 bailout:
  log_error ("error at \"%s\", offset %u\n",
             where, (unsigned int)(p - p_start));
  if (result)
    {
      int i;

      for (i=0; result[i]; i++)
        gcry_mpi_release (result[i]);
      gcry_free (result);
    }
  gcry_free (cram_buffer);
  return NULL;
}

Project: gnupg
File: minip12.c

parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
                          int startoffset, size_t *r_consumed, const char *pw,
                          void (*certcb)(void*, const unsigned char*, size_t),
                          void *certcbarg, gcry_mpi_t **r_result,
                          int *r_badpass)
{
  struct tag_info ti;
  const unsigned char *p = buffer;
  const unsigned char *p_start = buffer;
  size_t n = length;
  const char *where;
  char salt[20];
  size_t saltlen;
  char iv[16];
  unsigned int iter;
  unsigned char *plain = NULL;
  int bad_pass = 0;
  unsigned char *cram_buffer = NULL;
  size_t consumed = 0; /* Number of bytes consumed from the orginal buffer. */
  int is_3des = 0;
  int is_pbes2 = 0;
  gcry_mpi_t *result = NULL;
  int result_count;

  if (r_result)
    *r_result = NULL;
  where = "start";
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.class != ASNCONTEXT || ti.tag)
    goto bailout;
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.tag != TAG_SEQUENCE)
    goto bailout;

  where = "bag.encryptedData.version";
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.tag != TAG_INTEGER || ti.length != 1 || *p != 0)
    goto bailout;
  p++; n--;
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.tag != TAG_SEQUENCE)
    goto bailout;

  where = "bag.encryptedData.data";
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_data)
      || memcmp (p, oid_data, DIM(oid_data)))
    goto bailout;
  p += DIM(oid_data);
  n -= DIM(oid_data);

  where = "bag.encryptedData.keyinfo";
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.class || ti.tag != TAG_SEQUENCE)
    goto bailout;
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (!ti.class && ti.tag == TAG_OBJECT_ID
      && ti.length == DIM(oid_pbeWithSHAAnd40BitRC2_CBC)
      && !memcmp (p, oid_pbeWithSHAAnd40BitRC2_CBC,
                  DIM(oid_pbeWithSHAAnd40BitRC2_CBC)))
    {
      p += DIM(oid_pbeWithSHAAnd40BitRC2_CBC);
      n -= DIM(oid_pbeWithSHAAnd40BitRC2_CBC);
    }
  else if (!ti.class && ti.tag == TAG_OBJECT_ID
      && ti.length == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)
      && !memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC,
                  DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)))
    {
      p += DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
      n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
      is_3des = 1;
    }
  else if (!ti.class && ti.tag == TAG_OBJECT_ID
           && ti.length == DIM(oid_pkcs5PBES2)
           && !memcmp (p, oid_pkcs5PBES2, ti.length))
    {
      p += ti.length;
      n -= ti.length;
      is_pbes2 = 1;
    }
  else
    goto bailout;

  if (is_pbes2)
    {
      where = "pkcs5PBES2-params";
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (ti.class || ti.tag != TAG_SEQUENCE)
        goto bailout;
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (ti.class || ti.tag != TAG_SEQUENCE)
        goto bailout;
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (!(!ti.class && ti.tag == TAG_OBJECT_ID
            && ti.length == DIM(oid_pkcs5PBKDF2)
            && !memcmp (p, oid_pkcs5PBKDF2, ti.length)))
        goto bailout; /* Not PBKDF2.  */
      p += ti.length;
      n -= ti.length;
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (ti.class || ti.tag != TAG_SEQUENCE)
        goto bailout;
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (!(!ti.class && ti.tag == TAG_OCTET_STRING
            && ti.length >= 8 && ti.length < sizeof salt))
        goto bailout;  /* No salt or unsupported length.  */
      saltlen = ti.length;
      memcpy (salt, p, saltlen);
      p += saltlen;
      n -= saltlen;

      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (!(!ti.class && ti.tag == TAG_INTEGER && ti.length))
        goto bailout;  /* No valid iteration count.  */
      for (iter=0; ti.length; ti.length--)
        {
          iter <<= 8;
          iter |= (*p++) & 0xff;
          n--;
        }
      /* Note: We don't support the optional parameters but assume
         that the algorithmIdentifier follows. */
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (ti.class || ti.tag != TAG_SEQUENCE)
        goto bailout;
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (!(!ti.class && ti.tag == TAG_OBJECT_ID
            && ti.length == DIM(oid_aes128_CBC)
            && !memcmp (p, oid_aes128_CBC, ti.length)))
        goto bailout; /* Not AES-128.  */
      p += ti.length;
      n -= ti.length;
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (!(!ti.class && ti.tag == TAG_OCTET_STRING && ti.length == sizeof iv))
        goto bailout; /* Bad IV.  */
      memcpy (iv, p, sizeof iv);
      p += sizeof iv;
      n -= sizeof iv;
    }
  else
    {
      where = "rc2or3des-params";
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (ti.class || ti.tag != TAG_SEQUENCE)
        goto bailout;
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (ti.class || ti.tag != TAG_OCTET_STRING
          || ti.length < 8 || ti.length > 20 )
        goto bailout;
      saltlen = ti.length;
      memcpy (salt, p, saltlen);
      p += saltlen;
      n -= saltlen;
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (ti.class || ti.tag != TAG_INTEGER || !ti.length )
        goto bailout;
      for (iter=0; ti.length; ti.length--)
        {
          iter <<= 8;
          iter |= (*p++) & 0xff;
          n--;
        }
    }

  where = "rc2or3desoraes-ciphertext";
  if (parse_tag (&p, &n, &ti))
    goto bailout;

  consumed = p - p_start;
  if (ti.class == ASNCONTEXT && ti.tag == 0 && ti.is_constructed && ti.ndef)
    {
      /* Mozilla exported certs now come with single byte chunks of
         octect strings.  (Mozilla Firefox 1.0.4).  Arghh. */
      where = "cram-rc2or3des-ciphertext";
      cram_buffer = cram_octet_string ( p, &n, &consumed);
      if (!cram_buffer)
        goto bailout;
      p = p_start = cram_buffer;
      if (r_consumed)
        *r_consumed = consumed;
      r_consumed = NULL; /* Ugly hack to not update that value any further. */
      ti.length = n;
    }
  else if (ti.class == ASNCONTEXT && ti.tag == 0 && ti.length )
    ;
  else
    goto bailout;

  log_info ("%lu bytes of %s encrypted text\n",ti.length,
            is_pbes2?"AES128":is_3des?"3DES":"RC2");

  plain = gcry_malloc_secure (ti.length);
  if (!plain)
    {
      log_error ("error allocating decryption buffer\n");
      goto bailout;
    }
  decrypt_block (p, plain, ti.length, salt, saltlen, iter,
                 iv, is_pbes2?16:0, pw,
                 is_pbes2 ? GCRY_CIPHER_AES128 :
                 is_3des  ? GCRY_CIPHER_3DES : GCRY_CIPHER_RFC2268_40,
                 bag_decrypted_data_p);
  n = ti.length;
  startoffset = 0;
  p_start = p = plain;

  where = "outer.outer.seq";
  if (parse_tag (&p, &n, &ti))
    {
      bad_pass = 1;
      goto bailout;
    }
  if (ti.class || ti.tag != TAG_SEQUENCE)
    {
      bad_pass = 1;
      goto bailout;
    }

  if (parse_tag (&p, &n, &ti))
    {
      bad_pass = 1;
      goto bailout;
    }

  /* Loop over all certificates inside the bag. */
  while (n)
    {
      int iscrlbag = 0;
      int iskeybag = 0;

      where = "certbag.nextcert";
      if (ti.class || ti.tag != TAG_SEQUENCE)
        goto bailout;

      where = "certbag.objectidentifier";
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (ti.class || ti.tag != TAG_OBJECT_ID)
        goto bailout;
      if ( ti.length == DIM(oid_pkcs_12_CertBag)
           && !memcmp (p, oid_pkcs_12_CertBag, DIM(oid_pkcs_12_CertBag)))
        {
          p += DIM(oid_pkcs_12_CertBag);
          n -= DIM(oid_pkcs_12_CertBag);
        }
      else if ( ti.length == DIM(oid_pkcs_12_CrlBag)
           && !memcmp (p, oid_pkcs_12_CrlBag, DIM(oid_pkcs_12_CrlBag)))
        {
          p += DIM(oid_pkcs_12_CrlBag);
          n -= DIM(oid_pkcs_12_CrlBag);
          iscrlbag = 1;
        }
      else if ( ti.length == DIM(oid_pkcs_12_keyBag)
           && !memcmp (p, oid_pkcs_12_keyBag, DIM(oid_pkcs_12_keyBag)))
        {
          /* The TrustedMIME plugin for MS Outlook started to create
             files with just one outer 3DES encrypted container and
             inside the certificates as well as the key. */
          p += DIM(oid_pkcs_12_keyBag);
          n -= DIM(oid_pkcs_12_keyBag);
          iskeybag = 1;
        }
      else
        goto bailout;

      where = "certbag.before.certheader";
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (ti.class != ASNCONTEXT || ti.tag)
        goto bailout;
      if (iscrlbag)
        {
          log_info ("skipping unsupported crlBag\n");
          p += ti.length;
          n -= ti.length;
        }
      else if (iskeybag && (result || !r_result))
        {
          log_info ("one keyBag already processed; skipping this one\n");
          p += ti.length;
          n -= ti.length;
        }
      else if (iskeybag)
        {
          int len;

          log_info ("processing simple keyBag\n");

          /* Fixme: This code is duplicated from parse_bag_data.  */
          if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
            goto bailout;
          if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER
              || ti.length != 1 || *p)
            goto bailout;
          p++; n--;
          if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
            goto bailout;
          len = ti.length;
          if (parse_tag (&p, &n, &ti))
            goto bailout;
          if (len < ti.nhdr)
            goto bailout;
          len -= ti.nhdr;
          if (ti.class || ti.tag != TAG_OBJECT_ID
              || ti.length != DIM(oid_rsaEncryption)
              || memcmp (p, oid_rsaEncryption,
                         DIM(oid_rsaEncryption)))
            goto bailout;
          p += DIM (oid_rsaEncryption);
          n -= DIM (oid_rsaEncryption);
          if (len < ti.length)
            goto bailout;
          len -= ti.length;
          if (n < len)
            goto bailout;
          p += len;
          n -= len;
          if ( parse_tag (&p, &n, &ti)
               || ti.class || ti.tag != TAG_OCTET_STRING)
            goto bailout;
          if ( parse_tag (&p, &n, &ti)
               || ti.class || ti.tag != TAG_SEQUENCE)
            goto bailout;
          len = ti.length;

          result = gcry_calloc (10, sizeof *result);
          if (!result)
            {
              log_error ( "error allocating result array\n");
              goto bailout;
            }
          result_count = 0;

          where = "reading.keybag.key-parameters";
          for (result_count = 0; len && result_count < 9;)
            {
              if ( parse_tag (&p, &n, &ti)
                   || ti.class || ti.tag != TAG_INTEGER)
                goto bailout;
              if (len < ti.nhdr)
                goto bailout;
              len -= ti.nhdr;
              if (len < ti.length)
                goto bailout;
              len -= ti.length;
              if (!result_count && ti.length == 1 && !*p)
                ; /* ignore the very first one if it is a 0 */
              else
                {
                  int rc;

                  rc = gcry_mpi_scan (result+result_count, GCRYMPI_FMT_USG, p,
                                      ti.length, NULL);
                  if (rc)
                    {
                      log_error ("error parsing key parameter: %s\n",
                                 gpg_strerror (rc));
                      goto bailout;
                    }
                  result_count++;
                }
              p += ti.length;
              n -= ti.length;
            }
          if (len)
            goto bailout;
        }
      else
        {
          log_info ("processing certBag\n");
          if (parse_tag (&p, &n, &ti))
            goto bailout;
          if (ti.class || ti.tag != TAG_SEQUENCE)
            goto bailout;
          if (parse_tag (&p, &n, &ti))
            goto bailout;
          if (ti.class || ti.tag != TAG_OBJECT_ID
              || ti.length != DIM(oid_x509Certificate_for_pkcs_12)
              || memcmp (p, oid_x509Certificate_for_pkcs_12,
                         DIM(oid_x509Certificate_for_pkcs_12)))
            goto bailout;
          p += DIM(oid_x509Certificate_for_pkcs_12);
          n -= DIM(oid_x509Certificate_for_pkcs_12);

          where = "certbag.before.octetstring";
          if (parse_tag (&p, &n, &ti))
            goto bailout;
          if (ti.class != ASNCONTEXT || ti.tag)
            goto bailout;
          if (parse_tag (&p, &n, &ti))
            goto bailout;
          if (ti.class || ti.tag != TAG_OCTET_STRING || ti.ndef)
            goto bailout;

          /* Return the certificate. */
          if (certcb)
            certcb (certcbarg, p, ti.length);

          p += ti.length;
          n -= ti.length;
        }

      /* Ugly hack to cope with the padding: Forget about the rest if
         that is less or equal to the cipher's block length.  We can
         reasonable assume that all valid data will be longer than
         just one block. */
      if (n <= (is_pbes2? 16:8))
        n = 0;

      /* Skip the optional SET with the pkcs12 cert attributes. */
      if (n)
        {
          where = "bag.attributes";
          if (parse_tag (&p, &n, &ti))
            goto bailout;
          if (!ti.class && ti.tag == TAG_SEQUENCE)
            ; /* No attributes. */
          else if (!ti.class && ti.tag == TAG_SET && !ti.ndef)
            { /* The optional SET. */
              p += ti.length;
              n -= ti.length;
              if (n <= (is_pbes2?16:8))
                n = 0;
              if (n && parse_tag (&p, &n, &ti))
                goto bailout;
            }
          else
            goto bailout;
        }
    }

  if (r_consumed)
    *r_consumed = consumed;
  gcry_free (plain);
  gcry_free (cram_buffer);
  if (r_result)
    *r_result = result;
  return 0;

 bailout:
  if (result)
    {
      int i;

      for (i=0; result[i]; i++)
        gcry_mpi_release (result[i]);
      gcry_free (result);
    }
  if (r_consumed)
    *r_consumed = consumed;
  gcry_free (plain);
  gcry_free (cram_buffer);
  log_error ("encryptedData error at \"%s\", offset %u\n",
             where, (unsigned int)((p - p_start)+startoffset));
  if (bad_pass)
    {
      /* Note, that the following string might be used by other programs
         to check for a bad passphrase; it should therefore not be
         translated or changed. */
      log_error ("possibly bad passphrase given\n");
      *r_badpass = 1;
    }
  return -1;
}

Project: gnupg
File: minip12.c

build_key_bag (unsigned char *buffer, size_t buflen, char *salt,
               const unsigned char *sha1hash, const char *keyidstr,
               size_t *r_length)
{
  size_t len[11], needed;
  unsigned char *p, *keybag;
  size_t keybaglen;

  /* Walk 11 steps down to collect the info: */

  /* 10. The data goes into an octet string. */
  needed = compute_tag_length (buflen);
  needed += buflen;

  /* 9. Prepend the algorithm identifier. */
  needed += DIM (data_3desiter2048);

  /* 8. Put a sequence around. */
  len[8] = needed;
  needed += compute_tag_length (needed);

  /* 7. Prepend a [0] tag. */
  len[7] = needed;
  needed += compute_tag_length (needed);

  /* 6b. The attributes which are appended at the end. */
  if (sha1hash)
    needed += DIM (data_attrtemplate) + 20;

  /* 6. Prepend the shroudedKeyBag OID. */
  needed += 2 + DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag);

  /* 5+4. Put all into two sequences. */
  len[5] = needed;
  needed += compute_tag_length ( needed);
  len[4] = needed;
  needed += compute_tag_length (needed);

  /* 3. This all goes into an octet string. */
  len[3] = needed;
  needed += compute_tag_length (needed);

  /* 2. Prepend another [0] tag. */
  len[2] = needed;
  needed += compute_tag_length (needed);

  /* 1. Prepend the data OID. */
  needed += 2 + DIM (oid_data);

  /* 0. Prepend another sequence. */
  len[0] = needed;
  needed += compute_tag_length (needed);

  /* Now that we have all length information, allocate a buffer. */
  p = keybag = gcry_malloc (needed);
  if (!keybag)
    {
      log_error ("error allocating buffer\n");
      return NULL;
    }

  /* Walk 11 steps up to store the data. */

  /* 0. Store the first sequence. */
  p = store_tag_length (p, TAG_SEQUENCE, len[0]);

  /* 1. Store the data OID. */
  p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data));
  memcpy (p, oid_data, DIM (oid_data));
  p += DIM (oid_data);

  /* 2. Store a [0] tag. */
  p = store_tag_length (p, 0xa0, len[2]);

  /* 3. And an octet string. */
  p = store_tag_length (p, TAG_OCTET_STRING, len[3]);

  /* 4+5. Two sequences. */
  p = store_tag_length (p, TAG_SEQUENCE, len[4]);
  p = store_tag_length (p, TAG_SEQUENCE, len[5]);

  /* 6. Store the shroudedKeyBag OID. */
  p = store_tag_length (p, TAG_OBJECT_ID,
                        DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag));
  memcpy (p, oid_pkcs_12_pkcs_8ShroudedKeyBag,
          DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag));
  p += DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag);

  /* 7. Store a [0] tag. */
  p = store_tag_length (p, 0xa0, len[7]);

  /* 8. Store a sequence. */
  p = store_tag_length (p, TAG_SEQUENCE, len[8]);

  /* 9. Now for the pre-encoded algorithm identifier and the salt. */
  memcpy (p, data_3desiter2048, DIM (data_3desiter2048));
  memcpy (p + DATA_3DESITER2048_SALT_OFF, salt, 8);
  p += DIM (data_3desiter2048);

  /* 10. And the octet string with the encrypted data. */
  p = store_tag_length (p, TAG_OCTET_STRING, buflen);
  memcpy (p, buffer, buflen);
  p += buflen;

  /* Append the attributes whose length we calculated at step 2b. */
  if (sha1hash)
    {
      int i;

      memcpy (p, data_attrtemplate, DIM (data_attrtemplate));
      for (i=0; i < 8; i++)
        p[DATA_ATTRTEMPLATE_KEYID_OFF+2*i+1] = keyidstr[i];
      p += DIM (data_attrtemplate);
      memcpy (p, sha1hash, 20);
      p += 20;
    }


  keybaglen = p - keybag;
  if (needed != keybaglen)
    log_debug ("length mismatch: %lu, %lu\n",
               (unsigned long)needed, (unsigned long)keybaglen);

  *r_length = keybaglen;
  return keybag;
}

Project: gnupg
File: minip12.c

parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
                size_t *r_consumed, const char *pw)
{
  int rc;
  struct tag_info ti;
  const unsigned char *p = buffer;
  const unsigned char *p_start = buffer;
  size_t n = length;
  const char *where;
  char salt[20];
  size_t saltlen;
  char iv[16];
  unsigned int iter;
  int len;
  unsigned char *plain = NULL;
  gcry_mpi_t *result = NULL;
  int result_count, i;
  unsigned char *cram_buffer = NULL;
  size_t consumed = 0; /* Number of bytes consumed from the orginal buffer. */
  int is_pbes2 = 0;

  where = "start";
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.class != ASNCONTEXT || ti.tag)
    goto bailout;
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.class || ti.tag != TAG_OCTET_STRING)
    goto bailout;

  consumed = p - p_start;
  if (ti.is_constructed && ti.ndef)
    {
      /* Mozilla exported certs now come with single byte chunks of
         octect strings.  (Mozilla Firefox 1.0.4).  Arghh. */
      where = "cram-data.outersegs";
      cram_buffer = cram_octet_string ( p, &n, &consumed);
      if (!cram_buffer)
        goto bailout;
      p = p_start = cram_buffer;
      if (r_consumed)
        *r_consumed = consumed;
      r_consumed = NULL; /* Ugly hack to not update that value any further. */
    }


  where = "data.outerseqs";
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.class || ti.tag != TAG_SEQUENCE)
    goto bailout;
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.class || ti.tag != TAG_SEQUENCE)
    goto bailout;

  where = "data.objectidentifier";
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.class || ti.tag != TAG_OBJECT_ID
      || ti.length != DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag)
      || memcmp (p, oid_pkcs_12_pkcs_8ShroudedKeyBag,
                 DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag)))
    goto bailout;
  p += DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag);
  n -= DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag);

  where = "shrouded,outerseqs";
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.class != ASNCONTEXT || ti.tag)
    goto bailout;
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.class || ti.tag != TAG_SEQUENCE)
    goto bailout;
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.class || ti.tag != TAG_SEQUENCE)
    goto bailout;
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.class == 0 && ti.tag == TAG_OBJECT_ID
      && ti.length == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)
      && !memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC,
                  DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)))
    {
      p += DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
      n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
    }
  else if (ti.class == 0 && ti.tag == TAG_OBJECT_ID
           && ti.length == DIM(oid_pkcs5PBES2)
           && !memcmp (p, oid_pkcs5PBES2, DIM(oid_pkcs5PBES2)))
    {
      p += DIM(oid_pkcs5PBES2);
      n -= DIM(oid_pkcs5PBES2);
      is_pbes2 = 1;
    }
  else
    goto bailout;

  if (is_pbes2)
    {
      where = "pkcs5PBES2-params";
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (ti.class || ti.tag != TAG_SEQUENCE)
        goto bailout;
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (ti.class || ti.tag != TAG_SEQUENCE)
        goto bailout;
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (!(!ti.class && ti.tag == TAG_OBJECT_ID
            && ti.length == DIM(oid_pkcs5PBKDF2)
            && !memcmp (p, oid_pkcs5PBKDF2, ti.length)))
        goto bailout; /* Not PBKDF2.  */
      p += ti.length;
      n -= ti.length;
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (ti.class || ti.tag != TAG_SEQUENCE)
        goto bailout;
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (!(!ti.class && ti.tag == TAG_OCTET_STRING
            && ti.length >= 8 && ti.length < sizeof salt))
        goto bailout;  /* No salt or unsupported length.  */
      saltlen = ti.length;
      memcpy (salt, p, saltlen);
      p += saltlen;
      n -= saltlen;

      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (!(!ti.class && ti.tag == TAG_INTEGER && ti.length))
        goto bailout;  /* No valid iteration count.  */
      for (iter=0; ti.length; ti.length--)
        {
          iter <<= 8;
          iter |= (*p++) & 0xff;
          n--;
        }
      /* Note: We don't support the optional parameters but assume
         that the algorithmIdentifier follows. */
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (ti.class || ti.tag != TAG_SEQUENCE)
        goto bailout;
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (!(!ti.class && ti.tag == TAG_OBJECT_ID
            && ti.length == DIM(oid_aes128_CBC)
            && !memcmp (p, oid_aes128_CBC, ti.length)))
        goto bailout; /* Not AES-128.  */
      p += ti.length;
      n -= ti.length;
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (!(!ti.class && ti.tag == TAG_OCTET_STRING && ti.length == sizeof iv))
        goto bailout; /* Bad IV.  */
      memcpy (iv, p, sizeof iv);
      p += sizeof iv;
      n -= sizeof iv;
    }
  else
    {
      where = "3des-params";
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (ti.class || ti.tag != TAG_SEQUENCE)
        goto bailout;
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (ti.class || ti.tag != TAG_OCTET_STRING
          || ti.length < 8 || ti.length > 20)
        goto bailout;
      saltlen = ti.length;
      memcpy (salt, p, saltlen);
      p += saltlen;
      n -= saltlen;
      if (parse_tag (&p, &n, &ti))
        goto bailout;
      if (ti.class || ti.tag != TAG_INTEGER || !ti.length )
        goto bailout;
      for (iter=0; ti.length; ti.length--)
        {
          iter <<= 8;
          iter |= (*p++) & 0xff;
          n--;
        }
    }

  where = "3desoraes-ciphertext";
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (ti.class || ti.tag != TAG_OCTET_STRING || !ti.length )
    goto bailout;

  log_info ("%lu bytes of %s encrypted text\n",
            ti.length, is_pbes2? "AES128":"3DES");

  plain = gcry_malloc_secure (ti.length);
  if (!plain)
    {
      log_error ("error allocating decryption buffer\n");
      goto bailout;
    }
  consumed += p - p_start + ti.length;
  decrypt_block (p, plain, ti.length, salt, saltlen, iter,
                 iv, is_pbes2? 16:0, pw,
                 is_pbes2? GCRY_CIPHER_AES128 : GCRY_CIPHER_3DES,
                 bag_data_p);
  n = ti.length;
  startoffset = 0;
  p_start = p = plain;

  where = "decrypted-text";
  if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
    goto bailout;
  if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER
      || ti.length != 1 || *p)
    goto bailout;
  p++; n--;
  if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
    goto bailout;
  len = ti.length;
  if (parse_tag (&p, &n, &ti))
    goto bailout;
  if (len < ti.nhdr)
    goto bailout;
  len -= ti.nhdr;
  if (ti.class || ti.tag != TAG_OBJECT_ID
      || ti.length != DIM(oid_rsaEncryption)
      || memcmp (p, oid_rsaEncryption,
                 DIM(oid_rsaEncryption)))
    goto bailout;
  p += DIM (oid_rsaEncryption);
  n -= DIM (oid_rsaEncryption);
  if (len < ti.length)
    goto bailout;
  len -= ti.length;
  if (n < len)
    goto bailout;
  p += len;
  n -= len;
  if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_OCTET_STRING)
    goto bailout;
  if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
    goto bailout;
  len = ti.length;

  result = gcry_calloc (10, sizeof *result);
  if (!result)
    {
      log_error ( "error allocating result array\n");
      goto bailout;
    }
  result_count = 0;

  where = "reading.key-parameters";
  for (result_count=0; len && result_count < 9;)
    {
      if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER)
        goto bailout;
      if (len < ti.nhdr)
        goto bailout;
      len -= ti.nhdr;
      if (len < ti.length)
        goto bailout;
      len -= ti.length;
      if (!result_count && ti.length == 1 && !*p)
        ; /* ignore the very first one if it is a 0 */
      else
        {
          rc = gcry_mpi_scan (result+result_count, GCRYMPI_FMT_USG, p,
                              ti.length, NULL);
          if (rc)
            {
              log_error ("error parsing key parameter: %s\n",
                         gpg_strerror (rc));
              goto bailout;
            }
          result_count++;
        }
      p += ti.length;
      n -= ti.length;
    }
  if (len)
    goto bailout;

  gcry_free (cram_buffer);
  if (r_consumed)
    *r_consumed = consumed;
  return result;

 bailout:
  gcry_free (plain);
  if (result)
    {
      for (i=0; result[i]; i++)
        gcry_mpi_release (result[i]);
      gcry_free (result);
    }
  gcry_free (cram_buffer);
  log_error ( "data error at \"%s\", offset %u\n",
              where, (unsigned int)((p - buffer) + startoffset));
  if (r_consumed)
    *r_consumed = consumed;
  return NULL;
}

Project: gnupg
File: minip12.c

p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen,
           const char *pw, const char *charset, size_t *r_length)
{
  unsigned char *buffer = NULL;
  size_t n, buflen;
  char salt[8];
  struct buffer_s seqlist[3];
  int seqlistidx = 0;
  unsigned char sha1hash[20];
  char keyidstr[8+1];
  char *pwbuf = NULL;
  size_t pwbufsize = 0;

  n = buflen = 0; /* (avoid compiler warning). */
  memset (sha1hash, 0, 20);
  *keyidstr = 0;

  if (charset && pw && *pw)
    {
      jnlib_iconv_t cd;
      const char *inptr;
      char *outptr;
      size_t inbytes, outbytes;

      /* We assume that the converted passphrase is at max 2 times
         longer than its utf-8 encoding. */
      pwbufsize = strlen (pw)*2 + 1;
      pwbuf = gcry_malloc_secure (pwbufsize);
      if (!pwbuf)
        {
          log_error ("out of secure memory while converting passphrase\n");
          goto failure;
        }

      cd = jnlib_iconv_open (charset, "utf-8");
      if (cd == (jnlib_iconv_t)(-1))
        {
          log_error ("can't convert passphrase to"
                     " requested charset '%s': %s\n",
                     charset, strerror (errno));
          gcry_free (pwbuf);
          pwbuf = NULL;
          goto failure;
        }

      inptr = pw;
      inbytes = strlen (pw);
      outptr = pwbuf;
      outbytes = pwbufsize - 1;
      if ( jnlib_iconv (cd, (const char **)&inptr, &inbytes,
                      &outptr, &outbytes) == (size_t)-1)
        {
          log_error ("error converting passphrase to"
                     " requested charset '%s': %s\n",
                     charset, strerror (errno));
          gcry_free (pwbuf);
          pwbuf = NULL;
          jnlib_iconv_close (cd);
          goto failure;
        }
      *outptr = 0;
      jnlib_iconv_close (cd);
      pw = pwbuf;
    }


  if (cert && certlen)
    {
      /* Calculate the hash value we need for the bag attributes. */
      gcry_md_hash_buffer (GCRY_MD_SHA1, sha1hash, cert, certlen);
      sprintf (keyidstr, "%02x%02x%02x%02x",
               sha1hash[16], sha1hash[17], sha1hash[18], sha1hash[19]);

      /* Encode the certificate. */
      buffer = build_cert_sequence (cert, certlen, sha1hash, keyidstr,
                                    &buflen);
      if (!buffer)
        goto failure;

      /* Encrypt it. */
      gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
      crypt_block (buffer, buflen, salt, 8, 2048, NULL, 0, pw,
                   GCRY_CIPHER_RFC2268_40, 1);

      /* Encode the encrypted stuff into a bag. */
      seqlist[seqlistidx].buffer = build_cert_bag (buffer, buflen, salt, &n);
      seqlist[seqlistidx].length = n;
      gcry_free (buffer);
      buffer = NULL;
      if (!seqlist[seqlistidx].buffer)
        goto failure;
      seqlistidx++;
    }


  if (kparms)
    {
      /* Encode the key. */
      buffer = build_key_sequence (kparms, 0, &buflen);
      if (!buffer)
        goto failure;

      /* Encrypt it. */
      gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
      crypt_block (buffer, buflen, salt, 8, 2048, NULL, 0,
                   pw, GCRY_CIPHER_3DES, 1);

      /* Encode the encrypted stuff into a bag. */
      if (cert && certlen)
        seqlist[seqlistidx].buffer = build_key_bag (buffer, buflen, salt,
                                                    sha1hash, keyidstr, &n);
      else
        seqlist[seqlistidx].buffer = build_key_bag (buffer, buflen, salt,
                                                    NULL, NULL, &n);
      seqlist[seqlistidx].length = n;
      gcry_free (buffer);
      buffer = NULL;
      if (!seqlist[seqlistidx].buffer)
        goto failure;
      seqlistidx++;
    }

  seqlist[seqlistidx].buffer = NULL;
  seqlist[seqlistidx].length = 0;

  buffer = create_final (seqlist, pw, &buflen);

 failure:
  if (pwbuf)
    {
      wipememory (pwbuf, pwbufsize);
      gcry_free (pwbuf);
    }
  for ( ; seqlistidx; seqlistidx--)
    gcry_free (seqlist[seqlistidx].buffer);

  *r_length = buffer? buflen : 0;
  return buffer;
}

Project: gnupg
File: minip12.c

create_final (struct buffer_s *sequences, const char *pw, size_t *r_length)
{
  int i;
  size_t needed = 0;
  size_t len[8], n;
  unsigned char *macstart;
  size_t maclen;
  unsigned char *result, *p;
  size_t resultlen;
  char salt[8];
  unsigned char keybuf[20];
  gcry_md_hd_t md;
  int rc;
  int with_mac = 1;


  /* 9 steps to create the pkcs#12 Krampf. */

  /* 8. The MAC. */
  /* We add this at step 0. */

  /* 7. All the buffers. */
  for (i=0; sequences[i].buffer; i++)
    needed += sequences[i].length;

  /* 6. This goes into a sequences. */
  len[6] = needed;
  n = compute_tag_length (needed);
  needed += n;

  /* 5. Encapsulate all in an octet string. */
  len[5] = needed;
  n = compute_tag_length (needed);
  needed += n;

  /* 4. And tag it with [0]. */
  len[4] = needed;
  n = compute_tag_length (needed);
  needed += n;

  /* 3. Prepend an data OID. */
  needed += 2 + DIM (oid_data);

  /* 2. Put all into a sequences. */
  len[2] = needed;
  n = compute_tag_length (needed);
  needed += n;

  /* 1. Prepend the version integer 3. */
  needed += 3;

  /* 0. And the final outer sequence. */
  if (with_mac)
    needed += DIM (data_mactemplate);
  len[0] = needed;
  n = compute_tag_length (needed);
  needed += n;

  /* Allocate a buffer. */
  result = gcry_malloc (needed);
  if (!result)
    {
      log_error ("error allocating buffer\n");
      return NULL;
    }
  p = result;

  /* 0. Store the very outer sequence. */
  p = store_tag_length (p, TAG_SEQUENCE, len[0]);

  /* 1. Store the version integer 3. */
  *p++ = TAG_INTEGER;
  *p++ = 1;
  *p++ = 3;

  /* 2. Store another sequence. */
  p = store_tag_length (p, TAG_SEQUENCE, len[2]);

  /* 3. Store the data OID. */
  p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data));
  memcpy (p, oid_data, DIM (oid_data));
  p += DIM (oid_data);

  /* 4. Next comes a context tag. */
  p = store_tag_length (p, 0xa0, len[4]);

  /* 5. And an octet string. */
  p = store_tag_length (p, TAG_OCTET_STRING, len[5]);

  /* 6. And the inner sequence. */
  macstart = p;
  p = store_tag_length (p, TAG_SEQUENCE, len[6]);

  /* 7. Append all the buffers. */
  for (i=0; sequences[i].buffer; i++)
    {
      memcpy (p, sequences[i].buffer, sequences[i].length);
      p += sequences[i].length;
    }

  if (with_mac)
    {
      /* Intermezzo to compute the MAC. */
      maclen = p - macstart;
      gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
      if (string_to_key (3, salt, 8, 2048, pw, 20, keybuf))
        {
          gcry_free (result);
          return NULL;
        }
      rc = gcry_md_open (&md, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC);
      if (rc)
        {
          log_error ("gcry_md_open failed: %s\n", gpg_strerror (rc));
          gcry_free (result);
          return NULL;
        }
      rc = gcry_md_setkey (md, keybuf, 20);
      if (rc)
        {
          log_error ("gcry_md_setkey failed: %s\n", gpg_strerror (rc));
          gcry_md_close (md);
          gcry_free (result);
          return NULL;
        }
      gcry_md_write (md, macstart, maclen);

      /* 8. Append the MAC template and fix it up. */
      memcpy (p, data_mactemplate, DIM (data_mactemplate));
      memcpy (p + DATA_MACTEMPLATE_SALT_OFF, salt, 8);
      memcpy (p + DATA_MACTEMPLATE_MAC_OFF, gcry_md_read (md, 0), 20);
      p += DIM (data_mactemplate);
      gcry_md_close (md);
    }

  /* Ready. */
  resultlen = p - result;
  if (needed != resultlen)
    log_debug ("length mismatch: %lu, %lu\n",
               (unsigned long)needed, (unsigned long)resultlen);

  *r_length = resultlen;
  return result;
}

Project: gnupg
File: minip12.c

string_to_key (int id, char *salt, size_t saltlen, int iter, const char *pw,
               int req_keylen, unsigned char *keybuf)
{
  int rc, i, j;
  gcry_md_hd_t md;
  gcry_mpi_t num_b1 = NULL;
  int pwlen;
  unsigned char hash[20], buf_b[64], buf_i[128], *p;
  size_t cur_keylen;
  size_t n;

  cur_keylen = 0;
  pwlen = strlen (pw);
  if (pwlen > 63/2)
    {
      log_error ("password too long\n");
      return -1;
    }

  if (saltlen < 8)
    {
      log_error ("salt too short\n");
      return -1;
    }

  /* Store salt and password in BUF_I */
  p = buf_i;
  for(i=0; i < 64; i++)
    *p++ = salt [i%saltlen];
  for(i=j=0; i < 64; i += 2)
    {
      *p++ = 0;
      *p++ = pw[j];
      if (++j > pwlen) /* Note, that we include the trailing zero */
        j = 0;
    }

  for (;;)
    {
      rc = gcry_md_open (&md, GCRY_MD_SHA1, 0);
      if (rc)
        {
          log_error ( "gcry_md_open failed: %s\n", gpg_strerror (rc));
          return rc;
        }
      for(i=0; i < 64; i++)
        gcry_md_putc (md, id);
      gcry_md_write (md, buf_i, 128);
      memcpy (hash, gcry_md_read (md, 0), 20);
      gcry_md_close (md);
      for (i=1; i < iter; i++)
        gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash, 20);

      for (i=0; i < 20 && cur_keylen < req_keylen; i++)
        keybuf[cur_keylen++] = hash[i];
      if (cur_keylen == req_keylen)
        {
          gcry_mpi_release (num_b1);
          return 0; /* ready */
        }

      /* need more bytes. */
      for(i=0; i < 64; i++)
        buf_b[i] = hash[i % 20];
      rc = gcry_mpi_scan (&num_b1, GCRYMPI_FMT_USG, buf_b, 64, &n);
      if (rc)
        {
          log_error ( "gcry_mpi_scan failed: %s\n", gpg_strerror (rc));
          return -1;
        }
      gcry_mpi_add_ui (num_b1, num_b1, 1);
      for (i=0; i < 128; i += 64)
        {
          gcry_mpi_t num_ij;

          rc = gcry_mpi_scan (&num_ij, GCRYMPI_FMT_USG, buf_i + i, 64, &n);
          if (rc)
            {
              log_error ( "gcry_mpi_scan failed: %s\n",
                       gpg_strerror (rc));
              return -1;
            }
          gcry_mpi_add (num_ij, num_ij, num_b1);
          gcry_mpi_clear_highbit (num_ij, 64*8);
          rc = gcry_mpi_print (GCRYMPI_FMT_USG, buf_i + i, 64, &n, num_ij);
          if (rc)
            {
              log_error ( "gcry_mpi_print failed: %s\n",
                          gpg_strerror (rc));
              return -1;
            }
          gcry_mpi_release (num_ij);
        }
    }
}

Project: gnupg
File: minip12.c

cert_cb (void *opaque, const unsigned char *cert, size_t certlen)
{
  printf ("got a certificate of %u bytes length\n", certlen);
}

Project: gnupg
File: minip12.c

p12_raw_build (gcry_mpi_t *kparms, int rawmode, size_t *r_length)
{
  unsigned char *buffer;
  size_t buflen;

  assert (rawmode == 1 || rawmode == 2);
  buffer = build_key_sequence (kparms, rawmode, &buflen);
  if (!buffer)
    return NULL;

  *r_length = buflen;
  return buffer;
}

Project: gnupg
File: minip12.c

store_tag_length (unsigned char *p, int tag, size_t n)
{
  if (tag == TAG_SEQUENCE)
    tag |= 0x20; /* constructed */

  *p++ = tag;
  if (n < 128)
    *p++ = n;
  else if (n < 256)
    {
      *p++ = 0x81;
      *p++ = n;
    }
  else if (n < 65536)
    {
      *p++ = 0x82;
      *p++ = n >> 8;
      *p++ = n;
    }

  return p;
}