From 2d9592e78f4978307e378e07d6c170a28000a494 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 28 May 2020 15:55:54 +0200 Subject: [PATCH] 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 --- doc/DETAILS | 2 + tools/gpg-card.c | 96 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/doc/DETAILS b/doc/DETAILS index c22c14b72..348c64502 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -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 diff --git a/tools/gpg-card.c b/tools/gpg-card.c index 1facb5302..f89565ca6 100644 --- a/tools/gpg-card.c +++ b/tools/gpg-card.c @@ -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);