mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
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:
parent
5ea878274e
commit
5da6925a33
@ -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)
|
||||
|
36
sm/export.c
36
sm/export.c
@ -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,14 +563,11 @@ 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. */
|
||||
elems = "nedqp--u";
|
||||
const char *elems = "nedqp--u";
|
||||
|
||||
array = xtrycalloc (strlen(elems) + 1, sizeof *array);
|
||||
if (!array)
|
||||
{
|
||||
@ -594,7 +593,6 @@ sexp_to_kparms (gcry_sexp_t sexp)
|
||||
return 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);
|
||||
@ -603,7 +601,29 @@ sexp_to_kparms (gcry_sexp_t sexp)
|
||||
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);
|
||||
return array;
|
||||
}
|
||||
|
||||
|
500
sm/minip12.c
500
sm/minip12.c
@ -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
165
sm/t-minip12.c
Normal 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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user