sm: Add support to export ECC private keys.

* sm/minip12.c [TEST]: Remove test code.  Include util.h, tlv.h. and
openpgpdefs.h.  Remove the class and tag constants and replace them by
those from tlv.h.
(builder_add_oid, builder_add_mpi): New.
(build_key_sequence): Rename to ...
(build_rsa_key_sequence): this.
(build_ecc_key_sequence): New.
(p12_build): Call RSA or ECC builder.
(p12_raw_build): Ditto.
* sm/export.c (gpgsm_p12_export): Use correct armor header for ECC.
(sexp_to_kparms): Support ECC.

* sm/t-minip12.c: New to replace the former TEST code in minip12.h.
--

GnuPG-bug-id: 4921
This commit is contained in:
Werner Koch 2020-04-27 16:53:30 +02:00
parent 5ea878274e
commit 5da6925a33
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
4 changed files with 562 additions and 214 deletions

View File

@ -20,6 +20,9 @@
EXTRA_DIST = ChangeLog-2011 gpgsm-w32info.rc
bin_PROGRAMS = gpgsm
noinst_PROGRAMS = $(module_tests) $(module_maint_tests)
TESTS = $(module_tests)
AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS)
@ -65,6 +68,22 @@ gpgsm_LDADD = $(common_libs) ../common/libgpgrl.a \
$(LIBICONV) $(resource_objs) $(extra_sys_libs)
gpgsm_LDFLAGS = $(extra_bin_ldflags)
module_tests =
module_maint_tests = t-minip12
t_common_src =
t_common_ldadd = $(libcommon) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) \
$(GPG_ERROR_LIBS) $(LIBINTL)
t_minip12_CFLAGS = -DWITHOUT_NPTH=1 \
$(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS)
t_minip12_SOURCES = $(t_common_src) t-minip12.c minip12.c
t_minip12_LDADD = $(t_common_ldadd)
# Make sure that all libs are build before we use them. This is
# important for things like make -j2.
$(PROGRAMS): $(common_libs)

View File

@ -428,8 +428,11 @@ gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream, int rawmode)
opt.p12_charset);
}
if (rawmode == 0)
ctrl->pem_name = "PKCS12";
else if (gpgsm_get_key_algo_info (cert, NULL) == GCRY_PK_ECC)
ctrl->pem_name = "EC PRIVATE KEY";
else if (rawmode == 1)
ctrl->pem_name = "PRIVATE KEY";
else
@ -551,7 +554,6 @@ sexp_to_kparms (gcry_sexp_t sexp)
const char *s;
size_t n;
int idx;
const char *elems;
gcry_mpi_t *array;
list = gcry_sexp_find_token (sexp, "private-key", 0 );
@ -561,49 +563,67 @@ sexp_to_kparms (gcry_sexp_t sexp)
gcry_sexp_release (list);
list = l2;
name = gcry_sexp_nth_data (list, 0, &n);
if(!name || n != 3 || memcmp (name, "rsa", 3))
if (name && n == 3 && !memcmp (name, "rsa", 3))
{
gcry_sexp_release (list);
return NULL;
}
/* Parameter names used with RSA in the pkcs#12 order. */
const char *elems = "nedqp--u";
/* Parameter names used with RSA in the pkcs#12 order. */
elems = "nedqp--u";
array = xtrycalloc (strlen(elems) + 1, sizeof *array);
if (!array)
{
gcry_sexp_release (list);
return NULL;
}
for (idx=0, s=elems; *s; s++, idx++ )
{
if (*s == '-')
continue; /* Computed below */
l2 = gcry_sexp_find_token (list, s, 1);
if (l2)
array = xtrycalloc (strlen(elems) + 1, sizeof *array);
if (!array)
{
array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
gcry_sexp_release (l2);
}
if (!array[idx]) /* Required parameter not found or invalid. */
{
for (idx=0; array[idx]; idx++)
gcry_mpi_release (array[idx]);
xfree (array);
gcry_sexp_release (list);
return NULL;
}
for (idx=0, s=elems; *s; s++, idx++ )
{
if (*s == '-')
continue; /* Computed below */
l2 = gcry_sexp_find_token (list, s, 1);
if (l2)
{
array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
gcry_sexp_release (l2);
}
if (!array[idx]) /* Required parameter not found or invalid. */
{
for (idx=0; array[idx]; idx++)
gcry_mpi_release (array[idx]);
xfree (array);
gcry_sexp_release (list);
return NULL;
}
}
array[5] = gcry_mpi_snew (0); /* compute d mod (q-1) */
gcry_mpi_sub_ui (array[5], array[3], 1);
gcry_mpi_mod (array[5], array[2], array[5]);
array[6] = gcry_mpi_snew (0); /* compute d mod (p-1) */
gcry_mpi_sub_ui (array[6], array[4], 1);
gcry_mpi_mod (array[6], array[2], array[6]);
}
else if (name && n == 3 && !memcmp (name, "ecc", 3))
{
array = xtrycalloc (3 + 1, sizeof *array);
if (!array)
{
gcry_sexp_release (list);
return NULL;
}
if (gcry_sexp_extract_param (list, NULL, "/'curve'qd",
array+0, array+1, array+2, NULL))
{
xfree (array);
array = NULL; /* Error. */
}
}
else
{
array = NULL;
}
gcry_sexp_release (list);
array[5] = gcry_mpi_snew (0); /* compute d mod (q-1) */
gcry_mpi_sub_ui (array[5], array[3], 1);
gcry_mpi_mod (array[5], array[2], array[5]);
array[6] = gcry_mpi_snew (0); /* compute d mod (p-1) */
gcry_mpi_sub_ui (array[6], array[4], 1);
gcry_mpi_mod (array[6], array[2], array[6]);
return array;
}

View File

@ -18,6 +18,14 @@
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
/* References:
* RFC-7292 - PKCS #12: Personal Information Exchange Syntax v1.1
* RFC-8351 - The PKCS #8 EncryptedPrivateKeyInfo Media Type
* RFC-5958 - Asymmetric Key Packages
* RFC-3447 - PKCS #1: RSA Cryptography Specifications Version 2.1
* RFC-5915 - Elliptic Curve Private Key Structure
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
@ -28,15 +36,13 @@
#include <gcrypt.h>
#include <errno.h>
#ifdef TEST
#include <sys/stat.h>
#include <unistd.h>
#endif
#include <ksba.h>
#include "../common/util.h"
#include "../common/logging.h"
#include "../common/utf8conv.h"
#include "../common/tlv.h"
#include "../common/openpgpdefs.h" /* Only for openpgp_curve_to_oid. */
#include "minip12.h"
#ifndef DIM
@ -44,48 +50,6 @@
#endif
enum
{
UNIVERSAL = 0,
APPLICATION = 1,
ASNCONTEXT = 2,
PRIVATE = 3
};
enum
{
TAG_NONE = 0,
TAG_BOOLEAN = 1,
TAG_INTEGER = 2,
TAG_BIT_STRING = 3,
TAG_OCTET_STRING = 4,
TAG_NULL = 5,
TAG_OBJECT_ID = 6,
TAG_OBJECT_DESCRIPTOR = 7,
TAG_EXTERNAL = 8,
TAG_REAL = 9,
TAG_ENUMERATED = 10,
TAG_EMBEDDED_PDV = 11,
TAG_UTF8_STRING = 12,
TAG_REALTIVE_OID = 13,
TAG_SEQUENCE = 16,
TAG_SET = 17,
TAG_NUMERIC_STRING = 18,
TAG_PRINTABLE_STRING = 19,
TAG_TELETEX_STRING = 20,
TAG_VIDEOTEX_STRING = 21,
TAG_IA5_STRING = 22,
TAG_UTC_TIME = 23,
TAG_GENERALIZED_TIME = 24,
TAG_GRAPHIC_STRING = 25,
TAG_VISIBLE_STRING = 26,
TAG_GENERAL_STRING = 27,
TAG_UNIVERSAL_STRING = 28,
TAG_CHARACTER_STRING = 29,
TAG_BMP_STRING = 30
};
static unsigned char const oid_data[9] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 };
@ -179,6 +143,98 @@ struct tag_info
};
/* Wrapper around tlv_builder_add_ptr to add an OID. When we
* eventually put the whole tlv_builder stuff into Libksba, we can add
* such a function there. Right now we don't do this to avoid a
* dependency on Libksba. Function return 1 on error. */
static int
builder_add_oid (tlv_builder_t tb, int class, const char *oid)
{
gpg_error_t err;
unsigned char *der;
size_t derlen;
err = ksba_oid_from_str (oid, &der, &derlen);
if (err)
{
log_error ("%s: error converting '%s' to DER: %s\n",
__func__, oid, gpg_strerror (err));
return 1;
}
tlv_builder_add_val (tb, class, TAG_OBJECT_ID, der, derlen);
ksba_free (der);
return 0;
}
/* Wrapper around tlv_builder_add_ptr to add an MPI. TAG May either
* be OCTET_STRING or BIT_STRING. When we eventually put the whole
* tlv_builder stuff into Libksba, we can add such a function there.
* Right now we don't do this to avoid a dependency on Libksba.
* Function return 1 on error. STRIP is a hack to remove the first
* octet from the value. */
static int
builder_add_mpi (tlv_builder_t tb, int class, int tag, gcry_mpi_t mpi,
int strip)
{
int returncode;
gpg_error_t err;
const unsigned char *s;
unsigned char *freethis = NULL;
unsigned char *freethis2 = NULL;
unsigned int nbits;
size_t n;
if (gcry_mpi_get_flag (mpi, GCRYMPI_FLAG_OPAQUE))
{
s = gcry_mpi_get_opaque (mpi, &nbits);
n = (nbits+7)/8;
}
else
{
err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &freethis, &n, mpi);
if (err)
{
log_error ("%s: error converting MPI: %s\n",
__func__, gpg_strerror (err));
returncode = 1;
goto leave;
}
s = freethis;
}
if (tag == TAG_BIT_STRING)
{
freethis2 = xtrymalloc_secure (n + 1);
if (!freethis2)
{
err = gpg_error_from_syserror ();
log_error ("%s: error converting MPI: %s\n",
__func__, gpg_strerror (err));
returncode = 1;
goto leave;
}
freethis2[0] = 0;
memcpy (freethis2+1, s, n);
s = freethis2;
n++;
}
strip = !!strip;
if (strip && n < 2)
strip = 0;
tlv_builder_add_val (tb, class, tag, s+strip, n-strip);
returncode = 0;
leave:
xfree (freethis);
xfree (freethis2);
return returncode;
}
/* Parse the buffer at the address BUFFER which is of SIZE and return
the tag and the length part from the TLV triplet. Update BUFFER
and SIZE on success. Checks that the encoded length does not
@ -250,7 +306,7 @@ parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti)
ti->length = len;
}
if (ti->class == UNIVERSAL && !ti->tag)
if (ti->class == CLASS_UNIVERSAL && !ti->tag)
ti->length = 0;
if (ti->length > length)
@ -301,7 +357,7 @@ cram_octet_string (const unsigned char *input, size_t *length,
{
if (parse_tag (&s, &n, &ti))
goto bailout;
if (ti.class == UNIVERSAL && ti.tag == TAG_OCTET_STRING
if (ti.class == CLASS_UNIVERSAL && ti.tag == TAG_OCTET_STRING
&& !ti.ndef && !ti.is_constructed)
{
memcpy (d, s, ti.length);
@ -309,7 +365,7 @@ cram_octet_string (const unsigned char *input, size_t *length,
d += ti.length;
n -= ti.length;
}
else if (ti.class == UNIVERSAL && !ti.tag && !ti.is_constructed)
else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed)
break; /* Ready */
else
goto bailout;
@ -532,7 +588,8 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen,
if (rc)
{
wipememory (buffer, length);
log_error ( "en/de-crytion failed: %s\n", gpg_strerror (rc));
log_error ("%scrytion failed (%zu bytes): %s\n",
encrypt?"en":"de", length, gpg_strerror (rc));
goto leave;
}
@ -645,7 +702,7 @@ bag_decrypted_data_p (const void *plaintext, size_t length)
/* { */
/* # warning debug code is enabled */
/* FILE *fp = fopen ("tmp-minip12-plain.der", "wb"); */
/* FILE *fp = fopen ("tmp-minip12-plain-data.der", "wb"); */
/* if (!fp || fwrite (p, n, 1, fp) != 1) */
/* exit (2); */
/* fclose (fp); */
@ -693,7 +750,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
where = "start";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != ASNCONTEXT || ti.tag)
if (ti.class != CLASS_CONTEXT || ti.tag)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
@ -853,7 +910,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
goto bailout;
consumed = p - p_start;
if (ti.class == ASNCONTEXT && ti.tag == 0 && ti.is_constructed && ti.ndef)
if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed && ti.ndef)
{
/* Mozilla exported certs now come with single byte chunks of
octet strings. (Mozilla Firefox 1.0.4). Arghh. */
@ -867,7 +924,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
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 if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.length )
;
else
goto bailout;
@ -952,7 +1009,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
where = "certbag.before.certheader";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != ASNCONTEXT || ti.tag)
if (ti.class != CLASS_CONTEXT || ti.tag)
goto bailout;
if (iscrlbag)
{
@ -1071,7 +1128,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
where = "certbag.before.octetstring";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != ASNCONTEXT || ti.tag)
if (ti.class != CLASS_CONTEXT || ti.tag)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
@ -1203,7 +1260,7 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
where = "start";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != ASNCONTEXT || ti.tag)
if (ti.class != CLASS_CONTEXT || ti.tag)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
@ -1250,7 +1307,7 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
where = "shrouded,outerseqs";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != ASNCONTEXT || ti.tag)
if (ti.class != CLASS_CONTEXT || ti.tag)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
@ -1624,11 +1681,11 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != ASNCONTEXT || ti.tag)
if (ti.class != CLASS_CONTEXT || ti.tag)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != UNIVERSAL || ti.tag != TAG_OCTET_STRING)
if (ti.class != CLASS_UNIVERSAL || ti.tag != TAG_OCTET_STRING)
goto bailout;
if (ti.is_constructed && ti.ndef)
@ -1645,7 +1702,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
where = "bags";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != UNIVERSAL || ti.tag != TAG_SEQUENCE)
if (ti.class != CLASS_UNIVERSAL || ti.tag != TAG_SEQUENCE)
goto bailout;
bagseqndef = ti.ndef;
bagseqlength = ti.length;
@ -1655,9 +1712,10 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
where = "bag-sequence";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (bagseqndef && ti.class == UNIVERSAL && !ti.tag && !ti.is_constructed)
if (bagseqndef && ti.class == CLASS_UNIVERSAL
&& !ti.tag && !ti.is_constructed)
break; /* Ready */
if (ti.class != UNIVERSAL || ti.tag != TAG_SEQUENCE)
if (ti.class != CLASS_UNIVERSAL || ti.tag != TAG_SEQUENCE)
goto bailout;
if (!bagseqndef)
@ -1737,7 +1795,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
/* Need to skip the Null Tag. */
if (parse_tag (&p, &n, &ti))
goto bailout;
if (!(ti.class == UNIVERSAL && !ti.tag && !ti.is_constructed))
if (!(ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed))
goto bailout;
}
}
@ -1960,36 +2018,36 @@ create_final (struct buffer_s *sequences, const char *pw, size_t *r_length)
/* Build a DER encoded SEQUENCE with the key:
SEQUENCE {
INTEGER 0
SEQUENCE {
OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
NULL
}
OCTET STRING, encapsulates {
SEQUENCE {
INTEGER 0
INTEGER
INTEGER
INTEGER
INTEGER
INTEGER
INTEGER
INTEGER
INTEGER
}
}
}
MODE controls what is being generated:
0 - As described above
1 - Ditto but without the padding
2 - Only the inner part (pkcs#1)
*/
*
* SEQUENCE { -- OneAsymmetricKey (RFC-5958)
* INTEGER 0
* SEQUENCE {
* OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
* NULL
* }
* OCTET STRING, encapsulates {
* SEQUENCE { -- RSAPrivateKey (RFC-3447)
* INTEGER 0 -- Version
* INTEGER -- n
* INTEGER -- e
* INTEGER -- d
* INTEGER -- p
* INTEGER -- q
* INTEGER -- d mod (p-1)
* INTEGER -- d mod (q-1)
* INTEGER -- q^-1 mod p
* }
* }
* }
*
* MODE controls what is being generated:
* 0 - As described above
* 1 - Ditto but without the padding
* 2 - Only the inner part (pkcs#1)
*/
static unsigned char *
build_key_sequence (gcry_mpi_t *kparms, int mode, size_t *r_length)
build_rsa_key_sequence (gcry_mpi_t *kparms, int mode, size_t *r_length)
{
int rc, i;
size_t needed, n;
@ -2120,6 +2178,156 @@ build_key_sequence (gcry_mpi_t *kparms, int mode, size_t *r_length)
}
/* Build a DER encoded SEQUENCE for an ECC key:
*
* SEQUENCE { -- OneAsymmetricKey (RFC-5958)
* INTEGER 0
* SEQUENCE {
* OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1)
* OBJECT IDENTIFIER -- curvename
* }
* OCTET STRING, encapsulates {
* SEQUENCE { -- ECPrivateKey
* INTEGER 1 -- version
* OCTET STRING -- privateKey
* [1] {
* BIT STRING - publicKey
* }
* }
* }
* }
*
* For details see RFC-5480 and RFC-5915 (ECparameters are not created).
*
* KPARMS[0] := Opaque MPI with the curve name as dotted-decimal string.
* KPARMS[1] := Opaque MPI with the pgublic key (q)
* KPARMS[2] := Opaque MPI with the private key (d)
* MODE controls what is being generated:
* 0 - As described above
* 1 - Ditto but without the extra padding needed for pcsk#12
* 2 - Only the octet string (ECPrivateKey)
*/
static unsigned char *
build_ecc_key_sequence (gcry_mpi_t *kparms, int mode, size_t *r_length)
{
gpg_error_t err;
unsigned int nbits, n;
const unsigned char *s;
char *p;
tlv_builder_t tb;
void *result;
size_t resultlen;
const char *curve;
unsigned int curvebits;
int e;
int i;
int strip_one;
for (i=0; kparms[i]; i++)
;
if (i != 3)
{
log_error ("%s: invalid number of parameters\n", __func__);
return NULL;
}
s = gcry_mpi_get_opaque (kparms[0], &nbits);
n = (nbits+7)/8;
p = xtrymalloc (n + 1);
if (!p)
{
err = gpg_error_from_syserror ();
log_error ("%s:%d: error getting parameter: %s\n",
__func__, __LINE__, gpg_strerror (err));
return NULL;
}
memcpy (p, s, n);
p[n] = 0;
/* We need to use our OpenPGP mapping to turn a curve name into its
* canonical numerical OID. We should have a Libgcrypt function to
* do this; see bug report #4926. */
curve = openpgp_curve_to_oid (p, &curvebits, NULL);
xfree (p);
if (!curve)
{
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
log_error ("%s:%d: error getting parameter: %s\n",
__func__, __LINE__, gpg_strerror (err));
return NULL;
}
/* Unfortunately the private key D may come with a single leading
* zero byte. This is becuase at some point it was treated as
* signed MPI and the code made sure that it is always interpreted
* as unsigned. Fortunately we got the size of the curve and can
* detect such a case reliable. */
s = gcry_mpi_get_opaque (kparms[2], &nbits);
n = (nbits+7)/8;
strip_one = (n == (curvebits+7)/8 + 1 && !*s);
tb = tlv_builder_new (1);
if (!tb)
{
err = gpg_error_from_syserror ();
log_error ("%s:%d: error creating new TLV builder: %s\n",
__func__, __LINE__, gpg_strerror (err));
return NULL;
}
e = 0;
tlv_builder_add_tag (tb, 0, TAG_SEQUENCE);
tlv_builder_add_ptr (tb, 0, TAG_INTEGER, "\0", 1);
tlv_builder_add_tag (tb, 0, TAG_SEQUENCE);
e|= builder_add_oid (tb, 0, "1.2.840.10045.2.1");
e|= builder_add_oid (tb, 0, curve);
tlv_builder_add_end (tb);
tlv_builder_add_tag (tb, 0, TAG_OCTET_STRING);
tlv_builder_add_tag (tb, 0, TAG_SEQUENCE);
tlv_builder_add_ptr (tb, 0, TAG_INTEGER, "\x01", 1);
e|= builder_add_mpi (tb, 0, TAG_OCTET_STRING, kparms[2], strip_one);
tlv_builder_add_tag (tb, CLASS_CONTEXT, 1);
e|= builder_add_mpi (tb, 0, TAG_BIT_STRING, kparms[1], 0);
tlv_builder_add_end (tb);
tlv_builder_add_end (tb);
tlv_builder_add_end (tb);
tlv_builder_add_end (tb);
err = tlv_builder_finalize (tb, &result, &resultlen);
if (err || e)
{
if (!err)
err = gpg_error (GPG_ERR_GENERAL);
log_error ("%s:%d: tlv building failed: %s\n",
__func__, __LINE__, gpg_strerror (err));
return NULL;
}
/* Append some pad characters if needed. */
if (!mode && (n = 8 - resultlen % 8))
{
p = xtrymalloc_secure (resultlen + n);
if (!p)
{
err = gpg_error_from_syserror ();
log_error ("%s:%d: error allocating buffer: %s\n",
__func__, __LINE__, gpg_strerror (err));
xfree (result);
return NULL;
}
memcpy (p, result, resultlen);
xfree (result);
result = p;
p = (unsigned char*)result + resultlen;
for (i=0; i < n; i++, resultlen++)
*p++ = n;
}
*r_length = resultlen;
return result;
}
static unsigned char *
build_key_bag (unsigned char *buffer, size_t buflen, char *salt,
@ -2564,7 +2772,16 @@ p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen,
if (kparms)
{
/* Encode the key. */
buffer = build_key_sequence (kparms, 0, &buflen);
int i;
/* Right, that is a stupid way to distinguish ECC from RSA. */
for (i=0; kparms[i]; i++)
;
if (i == 3 && gcry_mpi_get_flag (kparms[0], GCRYMPI_FLAG_OPAQUE))
buffer = build_ecc_key_sequence (kparms, 0, &buflen);
else
buffer = build_rsa_key_sequence (kparms, 0, &buflen);
if (!buffer)
goto failure;
@ -2616,94 +2833,21 @@ p12_raw_build (gcry_mpi_t *kparms, int rawmode, size_t *r_length)
{
unsigned char *buffer;
size_t buflen;
int i;
assert (rawmode == 1 || rawmode == 2);
buffer = build_key_sequence (kparms, rawmode, &buflen);
log_assert (rawmode == 1 || rawmode == 2);
/* Right, that is a stupid way to distinguish ECC from RSA. */
for (i=0; kparms[i]; i++)
;
if (gcry_mpi_get_flag (kparms[0], GCRYMPI_FLAG_OPAQUE))
buffer = build_ecc_key_sequence (kparms, rawmode, &buflen);
else
buffer = build_rsa_key_sequence (kparms, rawmode, &buflen);
if (!buffer)
return NULL;
*r_length = buflen;
return buffer;
}
#ifdef TEST
static void
cert_cb (void *opaque, const unsigned char *cert, size_t certlen)
{
printf ("got a certificate of %u bytes length\n", certlen);
}
int
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;
}
/*
Local Variables:
compile-command: "gcc -Wall -O0 -g -DTEST=1 -o minip12 minip12.c ../common/libcommon.a -L /usr/local/lib -lgcrypt -lgpg-error"
End:
*/
#endif /* TEST */

165
sm/t-minip12.c Normal file
View File

@ -0,0 +1,165 @@
/* t-minip12.c - Test driver for minip12.c
* Copyright (C) 2020 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include "../common/util.h"
#include "minip12.h"
#define PGM "t-minip12"
static int verbose;
static int debug;
static void
cert_cb (void *opaque, const unsigned char *cert, size_t certlen)
{
(void)opaque;
(void)cert;
if (verbose)
log_info ("got a certificate of %zu bytes length\n", certlen);
}
int
main (int argc, char **argv)
{
int last_argc = -1;
char const *name = NULL;
char const *pass = NULL;
FILE *fp;
struct stat st;
unsigned char *buf;
size_t buflen;
gcry_mpi_t *result;
int badpass;
char *curve = NULL;
if (argc)
{ argc--; argv++; }
while (argc && last_argc != argc )
{
last_argc = argc;
if (!strcmp (*argv, "--"))
{
argc--; argv++;
break;
}
else if (!strcmp (*argv, "--help"))
{
fputs ("usage: " PGM " <pkcs12file> [<passphrase>]\n"
"Options:\n"
" --verbose print timings etc.\n"
" --debug flyswatter\n"
, stdout);
exit (0);
}
else if (!strcmp (*argv, "--verbose"))
{
verbose++;
argc--; argv++;
}
else if (!strcmp (*argv, "--debug"))
{
verbose += 2;
debug++;
argc--; argv++;
}
else if (!strncmp (*argv, "--", 2))
{
fprintf (stderr, PGM ": unknown option '%s'\n", *argv);
exit (1);
}
}
if (argc == 1)
{
name = argv[0];
pass = "";
}
else if (argc == 2)
{
name = argv[0];
pass = argv[1];
}
else
{
fprintf (stderr, "usage: " PGM " <file> [<passphrase>]\n");
exit (1);
}
gcry_control (GCRYCTL_DISABLE_SECMEM, NULL);
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, NULL);
fp = fopen (name, "rb");
if (!fp)
{
fprintf (stderr, PGM": can't open '%s': %s\n", name, strerror (errno));
return 1;
}
if (fstat (fileno(fp), &st))
{
fprintf (stderr, PGM": can't stat '%s': %s\n", name, 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", name, strerror (errno));
return 1;
}
fclose (fp);
result = p12_parse (buf, buflen, pass, cert_cb, NULL, &badpass, &curve);
if (result)
{
int i, rc;
unsigned char *tmpbuf;
if (curve)
log_info ("curve: %s\n", curve);
for (i=0; result[i]; i++)
{
rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &tmpbuf, NULL, result[i]);
if (rc)
log_error ("%d: [error printing number: %s]\n",
i, gpg_strerror (rc));
else
{
log_info ("%d: %s\n", i, tmpbuf);
gcry_free (tmpbuf);
}
}
}
return 0;
}