scd:p15: Factor the commonKeyAttributes parser out.

* scd/app-p15.c (read_ef_prkdf): Fix detection of unsupported key
 objects.  Factor some code out to ...
(parse_common_key_attr): new.
--
This commit is contained in:
Werner Koch 2021-01-27 13:11:19 +01:00
parent b08418d22c
commit 5bcbc8cee3
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
1 changed files with 206 additions and 134 deletions

View File

@ -1033,6 +1033,173 @@ parse_common_obj_attr (unsigned char const **buffer, size_t *size,
}
/* Parse the commonKeyAttributes. On success store the objid at
* (R_OBJID/R_OBJIDLEN), sets the key usage flags at USAGEFLAGS and
* the optiona key refrence at R_KEY_REFERENCE. The latter is only
* valid if true is also stored at R_KEY_REFERENCE_VALID.
*
* Example data:
*
* 21 30 12: SEQUENCE { -- commonKeyAttributes
* 23 04 1: OCTET STRING
* : 01
* 26 03 3: BIT STRING 6 unused bits
* : '1000000000'B (bit 9)
* 31 02 2: INTEGER 80 -- keyReference (optional)
* : }
*/
static gpg_error_t
parse_common_key_attr (unsigned char const **buffer, size_t *size,
unsigned char **r_objid, size_t *r_objidlen,
keyusage_flags_t *usageflags,
unsigned long *r_key_reference,
int *r_key_reference_valid)
{
gpg_error_t err;
int where;
int class, tag, constructed, ndef;
size_t objlen, hdrlen, nnn;
const unsigned char *ppp;
int ignore_eof = 0;
unsigned long ul;
const unsigned char *objid = NULL;
size_t objidlen;
unsigned long key_reference = 0;
int key_reference_valid = 0;
*r_objid = NULL;
*r_objidlen = 0;
memset (usageflags, 0, sizeof *usageflags);
*r_key_reference_valid = 0;
where = __LINE__;
err = parse_ber_header (buffer, size, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > *size || tag != TAG_SEQUENCE))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto leave;
ppp = *buffer;
nnn = objlen;
*buffer += objlen;
*size -= objlen;
/* Get the Id. */
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nnn
|| class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto leave;
objid = ppp;
objidlen = objlen;
ppp += objlen;
nnn -= objlen;
/* Get the KeyUsageFlags. */
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nnn
|| class != CLASS_UNIVERSAL || tag != TAG_BIT_STRING))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto leave;
err = parse_keyusage_flags (ppp, objlen, usageflags);
if (err)
goto leave;
ppp += objlen;
nnn -= objlen;
ignore_eof = 1; /* Remaining items are optional. */
/* Find the keyReference */
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && objlen > nnn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto leave;
if (class == CLASS_UNIVERSAL && tag == TAG_BOOLEAN)
{
/* Skip the native element. */
ppp += objlen;
nnn -= objlen;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && objlen > nnn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto leave;
}
if (class == CLASS_UNIVERSAL && tag == TAG_BIT_STRING)
{
/* Skip the accessFlags. */
ppp += objlen;
nnn -= objlen;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && objlen > nnn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto leave;
}
if (class == CLASS_UNIVERSAL && tag == TAG_INTEGER)
{
/* This is the keyReference. */
for (ul=0; objlen; objlen--)
{
ul <<= 8;
ul |= (*ppp++) & 0xff;
nnn--;
}
key_reference = ul;
key_reference_valid = 1;
}
leave:
if (ignore_eof && gpg_err_code (err) == GPG_ERR_EOF)
err = 0;
if (!err)
{
if (!objid || !objidlen)
err = gpg_error (GPG_ERR_INV_OBJ);
else
{
*r_objid = xtrymalloc (objidlen);
if (!*r_objid)
err = gpg_error_from_syserror ();
else
{
memcpy (*r_objid, objid, objidlen);
*r_objidlen = objidlen;
}
}
}
if (!err && key_reference_valid)
{
*r_key_reference = key_reference;
*r_key_reference_valid = 1;
}
if (err)
log_error ("p15: error parsing commonKeyAttributes at %d: %s\n",
where, gpg_strerror (err));
return err;
}
/* Read and parse the Private Key Directory Files. */
/*
6034 (privatekeys)
@ -1096,6 +1263,8 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
int recno = 1;
unsigned char *authid = NULL;
size_t authidlen = 0;
unsigned char *objid = NULL;
size_t objidlen = 0;
if (!fid)
return gpg_error (GPG_ERR_NO_DATA); /* No private keys. */
@ -1112,8 +1281,6 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
p = buffer;
n = buflen;
/* FIXME: This shares a LOT of code with read_ef_cdf! */
/* Loop over the records. We stop as soon as we detect a new record
starting with 0x00 or 0xff as these values are commonly used to
pad data blocks and are no valid ASN.1 encoding. Note the
@ -1126,25 +1293,37 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
const char *errstr = NULL;
prkdf_object_t prkdf = NULL;
unsigned long ul;
const unsigned char *objid;
size_t objidlen;
keyusage_flags_t usageflags;
unsigned long key_reference = 0;
int key_reference_valid = 0;
const char *s;
where = __LINE__;
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (err)
;
else if (objlen > n)
err = gpg_error (GPG_ERR_INV_OBJ);
else if (tag == TAG_SEQUENCE)
;
else if (class == CLASS_CONTEXT && tag == 0)
; /* Seen with CardOS. */
else if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
; /* PrivateRSAKeyAttributes */
else if (class == CLASS_CONTEXT)
{
switch (tag)
{
case 0: errstr = "EC key objects are not supported"; break;
case 1: errstr = "DH key objects are not supported"; break;
case 2: errstr = "DSA key objects are not supported"; break;
case 3: errstr = "KEA key objects are not supported"; break;
default: errstr = "unknown privateKeyObject"; break;
}
goto parse_error;
}
else
err = gpg_error (GPG_ERR_INV_OBJ);
{
err = gpg_error (GPG_ERR_INV_OBJ);
goto parse_error;
}
if (err)
{
@ -1152,6 +1331,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
gpg_strerror (err));
goto leave;
}
pp = p;
nn = objlen;
p += objlen;
@ -1166,105 +1346,14 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
/* Parse the commonKeyAttributes. */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nn || tag != TAG_SEQUENCE))
err = gpg_error (GPG_ERR_INV_OBJ);
xfree (objid);
err = parse_common_key_attr (&pp, &nn,
&objid, &objidlen,
&usageflags,
&key_reference, &key_reference_valid);
if (err)
goto parse_error;
{
const unsigned char *ppp = pp;
size_t nnn = objlen;
pp += objlen;
nn -= objlen;
/* Get the Id. */
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nnn
|| class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
objid = ppp;
objidlen = objlen;
ppp += objlen;
nnn -= objlen;
/* Get the KeyUsageFlags. */
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > nnn
|| class != CLASS_UNIVERSAL || tag != TAG_BIT_STRING))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
err = parse_keyusage_flags (ppp, objlen, &usageflags);
if (err)
goto parse_error;
ppp += objlen;
nnn -= objlen;
/* Find the keyReference */
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (gpg_err_code (err) == GPG_ERR_EOF)
goto leave_cki;
if (!err && objlen > nnn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
if (class == CLASS_UNIVERSAL && tag == TAG_BOOLEAN)
{
/* Skip the native element. */
ppp += objlen;
nnn -= objlen;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (gpg_err_code (err) == GPG_ERR_EOF)
goto leave_cki;
if (!err && objlen > nnn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
}
if (class == CLASS_UNIVERSAL && tag == TAG_BIT_STRING)
{
/* Skip the accessFlags. */
ppp += objlen;
nnn -= objlen;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (gpg_err_code (err) == GPG_ERR_EOF)
goto leave_cki;
if (!err && objlen > nnn)
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
}
if (class == CLASS_UNIVERSAL && tag == TAG_INTEGER)
{
/* Yep, this is the keyReference. */
for (ul=0; objlen; objlen--)
{
ul <<= 8;
ul |= (*ppp++) & 0xff;
nnn--;
}
key_reference = ul;
key_reference_valid = 1;
}
leave_cki:
;
}
log_assert (objid);
/* Skip subClassAttributes. */
where = __LINE__;
@ -1293,29 +1382,16 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && objlen > nn)
if (err)
;
else if (!err && objlen > nn)
err = gpg_error (GPG_ERR_INV_OBJ);
else if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
; /* A typeAttribute always starts with a sequence. */
else
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto parse_error;
if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
; /* RSA */
else if (class == CLASS_CONTEXT)
{
switch (tag)
{
case 0: errstr = "EC key objects are not supported"; break;
case 1: errstr = "DH key objects are not supported"; break;
case 2: errstr = "DSA key objects are not supported"; break;
case 3: errstr = "KEA key objects are not supported"; break;
default: errstr = "unknown privateKeyObject"; break;
}
goto parse_error;
}
else
{
err = gpg_error (GPG_ERR_INV_OBJ);
goto parse_error;
}
nn = objlen;
@ -1351,6 +1427,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
errstr = "invalid path reference";
goto parse_error;
}
/* Create a new PrKDF list item. */
prkdf = xtrycalloc (1, (sizeof *prkdf
- sizeof(unsigned short)
@ -1361,14 +1438,8 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
goto leave;
}
prkdf->objidlen = objidlen;
prkdf->objid = xtrymalloc (objidlen);
if (!prkdf->objid)
{
err = gpg_error_from_syserror ();
xfree (prkdf);
goto leave;
}
memcpy (prkdf->objid, objid, objidlen);
prkdf->objid = objid;
objid = NULL;
if (authid)
{
prkdf->authidlen = authidlen;
@ -1498,6 +1569,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
leave:
xfree (authid);
xfree (objid);
xfree (buffer);
if (err)
release_prkdflist (prkdflist);