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. */ /* Read and parse the Private Key Directory Files. */
/* /*
6034 (privatekeys) 6034 (privatekeys)
@ -1096,6 +1263,8 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
int recno = 1; int recno = 1;
unsigned char *authid = NULL; unsigned char *authid = NULL;
size_t authidlen = 0; size_t authidlen = 0;
unsigned char *objid = NULL;
size_t objidlen = 0;
if (!fid) if (!fid)
return gpg_error (GPG_ERR_NO_DATA); /* No private keys. */ 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; p = buffer;
n = buflen; 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 /* 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 starting with 0x00 or 0xff as these values are commonly used to
pad data blocks and are no valid ASN.1 encoding. Note the 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; const char *errstr = NULL;
prkdf_object_t prkdf = NULL; prkdf_object_t prkdf = NULL;
unsigned long ul; unsigned long ul;
const unsigned char *objid;
size_t objidlen;
keyusage_flags_t usageflags; keyusage_flags_t usageflags;
unsigned long key_reference = 0; unsigned long key_reference = 0;
int key_reference_valid = 0; int key_reference_valid = 0;
const char *s; const char *s;
where = __LINE__;
err = parse_ber_header (&p, &n, &class, &tag, &constructed, err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen); &ndef, &objlen, &hdrlen);
if (err) if (err)
; ;
else if (objlen > n) else if (objlen > n)
err = gpg_error (GPG_ERR_INV_OBJ); err = gpg_error (GPG_ERR_INV_OBJ);
else if (tag == TAG_SEQUENCE) else if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
; ; /* PrivateRSAKeyAttributes */
else if (class == CLASS_CONTEXT && tag == 0) else if (class == CLASS_CONTEXT)
; /* Seen with CardOS. */ {
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 else
err = gpg_error (GPG_ERR_INV_OBJ); {
err = gpg_error (GPG_ERR_INV_OBJ);
goto parse_error;
}
if (err) if (err)
{ {
@ -1152,6 +1331,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
gpg_strerror (err)); gpg_strerror (err));
goto leave; goto leave;
} }
pp = p; pp = p;
nn = objlen; nn = objlen;
p += objlen; p += objlen;
@ -1166,105 +1346,14 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
/* Parse the commonKeyAttributes. */ /* Parse the commonKeyAttributes. */
where = __LINE__; where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, xfree (objid);
&ndef, &objlen, &hdrlen); err = parse_common_key_attr (&pp, &nn,
if (!err && (objlen > nn || tag != TAG_SEQUENCE)) &objid, &objidlen,
err = gpg_error (GPG_ERR_INV_OBJ); &usageflags,
&key_reference, &key_reference_valid);
if (err) if (err)
goto parse_error; goto parse_error;
{ log_assert (objid);
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:
;
}
/* Skip subClassAttributes. */ /* Skip subClassAttributes. */
where = __LINE__; where = __LINE__;
@ -1293,29 +1382,16 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
where = __LINE__; where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen); &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); err = gpg_error (GPG_ERR_INV_OBJ);
if (err) if (err)
goto parse_error; 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; nn = objlen;
@ -1351,6 +1427,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
errstr = "invalid path reference"; errstr = "invalid path reference";
goto parse_error; goto parse_error;
} }
/* Create a new PrKDF list item. */ /* Create a new PrKDF list item. */
prkdf = xtrycalloc (1, (sizeof *prkdf prkdf = xtrycalloc (1, (sizeof *prkdf
- sizeof(unsigned short) - sizeof(unsigned short)
@ -1361,14 +1438,8 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
goto leave; goto leave;
} }
prkdf->objidlen = objidlen; prkdf->objidlen = objidlen;
prkdf->objid = xtrymalloc (objidlen); prkdf->objid = objid;
if (!prkdf->objid) objid = NULL;
{
err = gpg_error_from_syserror ();
xfree (prkdf);
goto leave;
}
memcpy (prkdf->objid, objid, objidlen);
if (authid) if (authid)
{ {
prkdf->authidlen = authidlen; prkdf->authidlen = authidlen;
@ -1498,6 +1569,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
leave: leave:
xfree (authid); xfree (authid);
xfree (objid);
xfree (buffer); xfree (buffer);
if (err) if (err)
release_prkdflist (prkdflist); release_prkdflist (prkdflist);