#include "cmslocal.h"
#include "secoid.h"
#include "secitem.h"
#include <security_asn1/secasn1.h>
#include <security_asn1/secerr.h>
SecCmsAttribute *
SecCmsAttributeCreate(PRArenaPool *poolp, SECOidTag oidtag, CSSM_DATA_PTR value, Boolean encoded)
{
SecCmsAttribute *attr;
CSSM_DATA_PTR copiedvalue;
void *mark;
PORT_Assert (poolp != NULL);
mark = PORT_ArenaMark (poolp);
attr = (SecCmsAttribute *)PORT_ArenaZAlloc(poolp, sizeof(SecCmsAttribute));
if (attr == NULL)
goto loser;
attr->typeTag = SECOID_FindOIDByTag(oidtag);
if (attr->typeTag == NULL)
goto loser;
if (SECITEM_CopyItem(poolp, &(attr->type), &(attr->typeTag->oid)) != SECSuccess)
goto loser;
if (value != NULL) {
if ((copiedvalue = SECITEM_AllocItem(poolp, NULL, value->Length)) == NULL)
goto loser;
if (SECITEM_CopyItem(poolp, copiedvalue, value) != SECSuccess)
goto loser;
if (SecCmsArrayAdd(poolp, (void ***)&(attr->values), (void *)copiedvalue) != SECSuccess)
goto loser;
}
attr->encoded = encoded;
PORT_ArenaUnmark (poolp, mark);
return attr;
loser:
PORT_Assert (mark != NULL);
PORT_ArenaRelease (poolp, mark);
return NULL;
}
OSStatus
SecCmsAttributeAddValue(PLArenaPool *poolp, SecCmsAttribute *attr, CSSM_DATA_PTR value)
{
CSSM_DATA_PTR copiedvalue;
void *mark;
PORT_Assert (poolp != NULL);
mark = PORT_ArenaMark(poolp);
if (value != NULL) {
if ((copiedvalue = SECITEM_AllocItem(poolp, NULL, value->Length)) == NULL)
goto loser;
if (SECITEM_CopyItem(poolp, copiedvalue, value) != SECSuccess)
goto loser;
if (SecCmsArrayAdd(poolp, (void ***)&(attr->values), (void *)copiedvalue) != SECSuccess)
goto loser;
SecCmsArraySort((void **)(attr->values), SecCmsUtilDERCompare, NULL, NULL);
}
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
PORT_Assert (mark != NULL);
PORT_ArenaRelease (poolp, mark);
return SECFailure;
}
SECOidTag
SecCmsAttributeGetType(SecCmsAttribute *attr)
{
SECOidData *typetag;
typetag = SECOID_FindOID(&(attr->type));
if (typetag == NULL)
return SEC_OID_UNKNOWN;
return typetag->offset;
}
CSSM_DATA_PTR
SecCmsAttributeGetValue(SecCmsAttribute *attr)
{
CSSM_DATA_PTR value;
if (attr == NULL)
return NULL;
value = attr->values[0];
if (value == NULL || value->Data == NULL || value->Length == 0)
return NULL;
if (attr->values[1] != NULL)
return NULL;
return value;
}
Boolean
SecCmsAttributeCompareValue(SecCmsAttribute *attr, CSSM_DATA_PTR av)
{
CSSM_DATA_PTR value;
if (attr == NULL)
return PR_FALSE;
value = SecCmsAttributeGetValue(attr);
return (value != NULL && value->Length == av->Length &&
PORT_Memcmp (value->Data, av->Data, value->Length) == 0);
}
static const SecAsn1Template *
cms_attr_choose_attr_value_template(void *src_or_dest, Boolean encoding, const char *buf, size_t len, void *dest)
{
const SecAsn1Template *theTemplate;
SecCmsAttribute *attribute;
SECOidData *oiddata;
Boolean encoded;
PORT_Assert (src_or_dest != NULL);
if (src_or_dest == NULL)
return NULL;
attribute = (SecCmsAttribute *)src_or_dest;
if (encoding && attribute->encoded)
return SEC_ASN1_GET(kSecAsn1AnyTemplate);
oiddata = attribute->typeTag;
if (oiddata == NULL) {
oiddata = SECOID_FindOID(&attribute->type);
attribute->typeTag = oiddata;
}
if (oiddata == NULL) {
encoded = PR_TRUE;
theTemplate = SEC_ASN1_GET(kSecAsn1AnyTemplate);
} else {
switch (oiddata->offset) {
case SEC_OID_PKCS9_SMIME_CAPABILITIES:
case SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE:
case SEC_OID_APPLE_HASH_AGILITY_V2:
default:
encoded = PR_TRUE;
theTemplate = SEC_ASN1_GET(kSecAsn1AnyTemplate);
break;
case SEC_OID_PKCS9_EMAIL_ADDRESS:
case SEC_OID_RFC1274_MAIL:
case SEC_OID_PKCS9_UNSTRUCTURED_NAME:
encoded = PR_FALSE;
theTemplate = SEC_ASN1_GET(kSecAsn1IA5StringTemplate);
break;
case SEC_OID_PKCS9_CONTENT_TYPE:
encoded = PR_FALSE;
theTemplate = SEC_ASN1_GET(kSecAsn1ObjectIDTemplate);
break;
case SEC_OID_PKCS9_MESSAGE_DIGEST:
case SEC_OID_APPLE_HASH_AGILITY:
encoded = PR_FALSE;
theTemplate = SEC_ASN1_GET(kSecAsn1OctetStringTemplate);
break;
case SEC_OID_PKCS9_SIGNING_TIME:
case SEC_OID_APPLE_EXPIRATION_TIME:
encoded = PR_FALSE;
theTemplate = SEC_ASN1_GET(kSecAsn1UTCTimeTemplate); break;
}
}
if (encoding) {
PORT_Assert (!encoded);
} else {
attribute->encoded = encoded;
}
return theTemplate;
}
static const SecAsn1TemplateChooserPtr cms_attr_chooser
= cms_attr_choose_attr_value_template;
const SecAsn1Template nss_cms_attribute_template[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(SecCmsAttribute) },
{ SEC_ASN1_OBJECT_ID,
offsetof(SecCmsAttribute,type) },
{ SEC_ASN1_DYNAMIC | SEC_ASN1_SET_OF,
offsetof(SecCmsAttribute,values),
&cms_attr_chooser },
{ 0 }
};
const SecAsn1Template nss_cms_set_of_attribute_template[] = {
{ SEC_ASN1_SET_OF, 0, nss_cms_attribute_template }
};
CSSM_DATA_PTR
SecCmsAttributeArrayEncode(PRArenaPool *poolp, SecCmsAttribute ***attrs, CSSM_DATA_PTR dest)
{
return SEC_ASN1EncodeItem (poolp, dest, (void *)attrs, nss_cms_set_of_attribute_template);
}
OSStatus
SecCmsAttributeArrayReorder(SecCmsAttribute **attrs)
{
return SecCmsArraySortByDER((void **)attrs, nss_cms_attribute_template, NULL);
}
SecCmsAttribute *
SecCmsAttributeArrayFindAttrByOidTag(SecCmsAttribute **attrs, SECOidTag oidtag, Boolean only)
{
SECOidData *oid;
SecCmsAttribute *attr1, *attr2;
if (attrs == NULL)
return NULL;
oid = SECOID_FindOIDByTag(oidtag);
if (oid == NULL)
return NULL;
while ((attr1 = *attrs++) != NULL) {
if (attr1->type.Length == oid->oid.Length && PORT_Memcmp (attr1->type.Data,
oid->oid.Data,
oid->oid.Length) == 0)
break;
}
if (attr1 == NULL)
return NULL;
if (!only)
return attr1;
while ((attr2 = *attrs++) != NULL) {
if (attr2->type.Length == oid->oid.Length && PORT_Memcmp (attr2->type.Data,
oid->oid.Data,
oid->oid.Length) == 0)
break;
}
if (attr2 != NULL)
return NULL;
return attr1;
}
OSStatus
SecCmsAttributeArrayAddAttr(PLArenaPool *poolp, SecCmsAttribute ***attrs, SecCmsAttribute *attr)
{
SecCmsAttribute *oattr;
void *mark;
SECOidTag type;
mark = PORT_ArenaMark(poolp);
type = SecCmsAttributeGetType(attr);
oattr = SecCmsAttributeArrayFindAttrByOidTag(*attrs, type, PR_FALSE);
PORT_Assert (oattr == NULL);
if (oattr != NULL)
goto loser;
if (SecCmsArrayAdd(poolp, (void ***)attrs, (void *)attr) != SECSuccess)
goto loser;
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
PORT_ArenaRelease(poolp, mark);
return SECFailure;
}
OSStatus
SecCmsAttributeArraySetAttr(PLArenaPool *poolp, SecCmsAttribute ***attrs, SECOidTag type, CSSM_DATA_PTR value, Boolean encoded)
{
SecCmsAttribute *attr;
void *mark;
mark = PORT_ArenaMark(poolp);
attr = SecCmsAttributeArrayFindAttrByOidTag(*attrs, type, PR_FALSE);
if (attr == NULL) {
attr = SecCmsAttributeCreate(poolp, type, value, encoded);
if (attr == NULL)
goto loser;
if (SecCmsArrayAdd(poolp, (void ***)attrs, (void *)attr) != SECSuccess)
goto loser;
} else {
attr->values[0] = value;
attr->encoded = encoded;
}
PORT_ArenaUnmark (poolp, mark);
return SECSuccess;
loser:
PORT_ArenaRelease (poolp, mark);
return SECFailure;
}