card: Allow to store and retrieve keyblocks in OpenPGP cards.

* tools/gpg-card.c: Include tlv.h.
(cmd_writecert): Add option --openpgp.
(cmd_readcert): Ditto.
--

We use the CERT object for this and encapsulate the key block in a CMS
object.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-05-28 15:55:54 +02:00
parent ed0759f39b
commit 2d9592e78f
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
2 changed files with 92 additions and 6 deletions

View File

@ -1509,6 +1509,8 @@ Status codes are:
1.3.6.1.4.1.11591.2.2 X.509 extensions
1.3.6.1.4.1.11591.2.2.1 standaloneCertificate
1.3.6.1.4.1.11591.2.2.2 wellKnownPrivateKey
1.3.6.1.4.1.11591.2.3 CMS contentType
1.3.6.1.4.1.11591.2.3.1 OpenPGP keyblock (as octet string)
1.3.6.1.4.1.11591.2.12242973 invalid encoded OID
#+end_example

View File

@ -41,6 +41,7 @@
#include "../common/ttyio.h"
#include "../common/server-help.h"
#include "../common/openpgpdefs.h"
#include "../common/tlv.h"
#include "gpg-card.h"
@ -1847,20 +1848,24 @@ cmd_writecert (card_info_t info, char *argstr)
{
gpg_error_t err;
int opt_clear;
int opt_openpgp;
char *certref_buffer = NULL;
char *certref;
char *data = NULL;
size_t datalen;
if (!info)
return print_help
("WRITECERT [--clear] CERTREF < FILE\n\n"
("WRITECERT [--clear] [--openpgp] CERTREF < FILE\n\n"
"Write a certificate for key 3. Unless --clear is given\n"
"the file argument is mandatory. The option --clear removes\n"
"the certificate from the card.",
"the certificate from the card. The option --openpgp expects\n"
"a keyblock and stores it encapsulated in a CMS container.",
APP_TYPE_OPENPGP, APP_TYPE_PIV, 0);
opt_clear = has_leading_option (argstr, "--clear");
opt_openpgp = has_leading_option (argstr, "--openpgp");
argstr = skip_options (argstr);
certref = argstr;
@ -1927,6 +1932,36 @@ cmd_writecert (card_info_t info, char *argstr)
goto leave;
}
if (opt_openpgp && !opt_clear)
{
tlv_builder_t tb;
void *tmpder;
size_t tmpderlen;
tb = tlv_builder_new (0);
if (!tb)
{
err = gpg_error_from_syserror ();
goto leave;
}
tlv_builder_add_tag (tb, 0, TAG_SEQUENCE);
tlv_builder_add_ptr (tb, 0, TAG_OBJECT_ID,
"\x2B\x06\x01\x04\x01\xDA\x47\x02\x03\x01", 10);
tlv_builder_add_tag (tb, CLASS_CONTEXT, 0);
tlv_builder_add_ptr (tb, 0, TAG_OCTET_STRING, data, datalen);
tlv_builder_add_end (tb);
tlv_builder_add_end (tb);
err = tlv_builder_finalize (tb, &tmpder, &tmpderlen);
if (err)
goto leave;
xfree (data);
data = tmpder;
datalen = tmpderlen;
}
err = scd_writecert (certref, data, datalen);
leave:
@ -1943,15 +1978,19 @@ cmd_readcert (card_info_t info, char *argstr)
char *certref_buffer = NULL;
char *certref;
void *data = NULL;
size_t datalen;
size_t datalen, dataoff;
const char *fname;
int opt_openpgp;
if (!info)
return print_help
("READCERT CERTREF > FILE\n\n"
"Read the certificate for key CERTREF and store it in FILE.",
("READCERT [--openpgp] CERTREF > FILE\n\n"
"Read the certificate for key CERTREF and store it in FILE.\n"
"With option \"--openpgp\" an OpenPGP keyblock is expected\n"
"and stored in FILE.\n",
APP_TYPE_OPENPGP, APP_TYPE_PIV, 0);
opt_openpgp = has_leading_option (argstr, "--openpgp");
argstr = skip_options (argstr);
certref = argstr;
@ -1987,11 +2026,56 @@ cmd_readcert (card_info_t info, char *argstr)
goto leave;
}
dataoff = 0;
err = scd_readcert (certref, &data, &datalen);
if (err)
goto leave;
err = put_data_to_file (fname, data, datalen);
if (opt_openpgp)
{
/* Check whether DATA contains an OpenPGP keyblock and put only
* this into FILE. If the data is something different, return
* an error. */
const unsigned char *p;
size_t n, objlen, hdrlen;
int class, tag, cons, ndef;
p = data;
n = datalen;
if (parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen))
goto not_openpgp;
if (!(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && cons))
goto not_openpgp; /* Does not start with a sequence. */
if (parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen))
goto not_openpgp;
if (!(class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !cons))
goto not_openpgp; /* No Object ID. */
if (objlen > n)
goto not_openpgp; /* Inconsistent lengths. */
if (objlen != 10
|| memcmp (p, "\x2B\x06\x01\x04\x01\xDA\x47\x02\x03\x01", objlen))
goto not_openpgp; /* Wrong Object ID. */
p += objlen;
n -= objlen;
if (parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen))
goto not_openpgp;
if (!(class == CLASS_CONTEXT && tag == 0 && cons))
goto not_openpgp; /* Not a [0] context tag. */
if (parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen))
goto not_openpgp;
if (!(class == CLASS_UNIVERSAL && tag == TAG_OCTET_STRING && !cons))
goto not_openpgp; /* Not an octet string. */
if (objlen > n)
goto not_openpgp; /* Inconsistent lengths. */
dataoff = p - (const unsigned char*)data;
datalen = objlen;
}
err = put_data_to_file (fname, (unsigned char*)data+dataoff, datalen);
goto leave;
not_openpgp:
err = gpg_error (GPG_ERR_WRONG_BLOB_TYPE);
leave:
xfree (data);