/* radare2 - LGPL - Copyright 2017-2022 - wargio */ #include #include "x509.h" extern void r_x509_name_json (PJ *pj, RX509Name *name); extern void r_x509_free_crl (RX509CertificateRevocationList *crl); extern void r_x509_crlentry_dump (RX509CRLEntry *crle, const char *pad, RStrBuf *sb); static bool r_pkcs7_parse_attributes(RPKCS7Attributes *attribute, RASN1Object *object); static bool r_pkcs7_parse_contentinfo(RPKCS7ContentInfo *ci, RASN1Object *object) { if (!ci || !object || object->list.length < 1 || !object->list.objects[0]) { return false; } ci->contentType = r_asn1_stringify_oid (object->list.objects[0]->sector, object->list.objects[0]->length); if (object->list.length > 1) { RASN1Object *obj1 = object->list.objects[1]; if (obj1) { ci->content = r_asn1_create_binary (obj1->sector, obj1->length); } } return true; } static bool r_pkcs7_parse_certificaterevocationlists(RPKCS7CertificateRevocationLists *crls, RASN1Object *object) { ut32 i; if (!crls || !object) { return false; } if (object->list.length > 0) { crls->elements = (RX509CertificateRevocationList **)calloc (object->list.length, sizeof (RX509CertificateRevocationList *)); if (!crls->elements) { return false; } crls->length = object->list.length; for (i = 0; i < crls->length; i++) { crls->elements[i] = r_x509_parse_crl (object->list.objects[i]); } } return true; } static void r_pkcs7_free_certificaterevocationlists(RPKCS7CertificateRevocationLists *crls) { ut32 i; if (crls) { for (i = 0; i < crls->length; i++) { r_x509_free_crl (crls->elements[i]); crls->elements[i] = NULL; } R_FREE (crls->elements); // Used internally pkcs #7, so it shouldn't free crls. } } static bool r_pkcs7_parse_extendedcertificatesandcertificates(RPKCS7ExtendedCertificatesAndCertificates *ecac, RASN1Object *object) { ut32 i; if (!ecac || !object) { return false; } if (object->list.length > 0) { ecac->elements = (RX509Certificate **)calloc (object->list.length, sizeof (RX509Certificate *)); if (!ecac->elements) { return false; } ecac->length = object->list.length; for (i = 0; i < ecac->length; i++) { ecac->elements[i] = r_x509_parse_certificate (object->list.objects[i]); object->list.objects[i] = NULL; } } return true; } static void r_pkcs7_free_extendedcertificatesandcertificates(RPKCS7ExtendedCertificatesAndCertificates *ecac) { ut32 i; if (ecac) { for (i = 0; i < ecac->length; i++) { r_x509_free_certificate (ecac->elements[i]); ecac->elements[i] = NULL; } R_FREE (ecac->elements); // Used internally pkcs #7, so it shouldn't free ecac. } } static bool r_pkcs7_parse_digestalgorithmidentifier(RPKCS7DigestAlgorithmIdentifiers *dai, RASN1Object *object) { ut32 i; if (!dai || !object) { return false; } if (object->list.length > 0) { dai->elements = (RX509AlgorithmIdentifier **)calloc (object->list.length, sizeof (RX509AlgorithmIdentifier *)); if (!dai->elements) { return false; } dai->length = object->list.length; for (i = 0; i < dai->length; i++) { // r_x509_parse_algorithmidentifier returns bool, // so i have to allocate before calling the function dai->elements[i] = (RX509AlgorithmIdentifier *)malloc (sizeof (RX509AlgorithmIdentifier)); //should i handle invalid memory? the function checks the pointer //or it should return if dai->elements[i] == NULL ? if (dai->elements[i]) { //Memset is needed to initialize to 0 the structure and avoid garbage. memset (dai->elements[i], 0, sizeof (RX509AlgorithmIdentifier)); r_x509_parse_algorithmidentifier (dai->elements[i], object->list.objects[i]); } } } return true; } static void r_pkcs7_free_digestalgorithmidentifier(RPKCS7DigestAlgorithmIdentifiers *dai) { ut32 i; if (dai) { for (i = 0; i < dai->length; i++) { if (dai->elements[i]) { r_x509_free_algorithmidentifier (dai->elements[i]); // r_x509_free_algorithmidentifier doesn't free the pointer // because on x509 the original use was internal. R_FREE (dai->elements[i]); } } R_FREE (dai->elements); // Used internally pkcs #7, so it shouldn't free dai. } } static void r_pkcs7_free_contentinfo(RPKCS7ContentInfo *ci) { if (ci) { r_asn1_binary_free (ci->content); r_asn1_string_free (ci->contentType); // Used internally pkcs #7, so it shouldn't free ci. } } static bool r_pkcs7_parse_issuerandserialnumber(RPKCS7IssuerAndSerialNumber *iasu, RASN1Object *object) { if (!iasu || !object || object->list.length < 2) { return false; } r_x509_parse_name (&iasu->issuer, object->list.objects[0]); RASN1Object *obj1 = object->list.objects[1]; if (obj1) { iasu->serialNumber = r_asn1_create_binary (obj1->sector, obj1->length); } return true; } static void r_pkcs7_free_issuerandserialnumber(RPKCS7IssuerAndSerialNumber *iasu) { if (iasu) { r_x509_free_name (&iasu->issuer); r_asn1_binary_free (iasu->serialNumber); // Used internally pkcs #7, so it shouldn't free iasu. } } static bool r_pkcs7_parse_signerinfo(RPKCS7SignerInfo *si, RASN1Object *object) { RASN1Object **elems; ut32 shift = 3; if (!si || !object || object->list.length < 5) { return false; } elems = object->list.objects; //Following RFC si->version = (ut32)elems[0]->sector[0]; r_pkcs7_parse_issuerandserialnumber (&si->issuerAndSerialNumber, elems[1]); r_x509_parse_algorithmidentifier (&si->digestAlgorithm, elems[2]); if (shift < object->list.length && elems[shift]->klass == CLASS_CONTEXT && elems[shift]->tag == 0) { r_pkcs7_parse_attributes (&si->authenticatedAttributes, elems[shift]); shift++; } if (shift < object->list.length) { r_x509_parse_algorithmidentifier (&si->digestEncryptionAlgorithm, elems[shift]); shift++; } if (shift < object->list.length) { RASN1Object *obj1 = object->list.objects[shift]; if (obj1) { si->encryptedDigest = r_asn1_create_binary (obj1->sector, obj1->length); shift++; } } if (shift < object->list.length) { RASN1Object *elem = elems[shift]; if (elem && elem->klass == CLASS_CONTEXT && elem->tag == 1) { r_pkcs7_parse_attributes (&si->unauthenticatedAttributes, elems[shift]); } } return true; } static void r_pkcs7_free_attribute(RPKCS7Attribute *attribute) { if (attribute) { r_asn1_binary_free (attribute->data); r_asn1_string_free (attribute->oid); free (attribute); } } static void r_pkcs7_free_attributes(RPKCS7Attributes *attributes) { ut32 i; if (attributes) { for (i = 0; i < attributes->length; i++) { r_pkcs7_free_attribute (attributes->elements[i]); } R_FREE (attributes->elements); // Used internally pkcs #7, so it shouldn't free attributes. } } static void r_pkcs7_free_signerinfo(RPKCS7SignerInfo *si) { if (si) { r_pkcs7_free_issuerandserialnumber (&si->issuerAndSerialNumber); r_x509_free_algorithmidentifier (&si->digestAlgorithm); r_pkcs7_free_attributes (&si->authenticatedAttributes); r_x509_free_algorithmidentifier (&si->digestEncryptionAlgorithm); r_asn1_binary_free (si->encryptedDigest); r_pkcs7_free_attributes (&si->unauthenticatedAttributes); free (si); } } static bool r_pkcs7_parse_signerinfos(RPKCS7SignerInfos *ss, RASN1Object *object) { ut32 i; if (!ss || !object) { return false; } if (object->list.length > 0) { ss->elements = (RPKCS7SignerInfo **)calloc (object->list.length, sizeof (RPKCS7SignerInfo *)); if (!ss->elements) { return false; } ss->length = object->list.length; for (i = 0; i < ss->length; i++) { // r_pkcs7_parse_signerinfo returns bool, // so i have to allocate before calling the function ss->elements[i] = R_NEW0 (RPKCS7SignerInfo); //should i handle invalid memory? the function checks the pointer //or it should return if si->elements[i] == NULL ? r_pkcs7_parse_signerinfo (ss->elements[i], object->list.objects[i]); } } return true; } static void r_pkcs7_free_signerinfos(RPKCS7SignerInfos *ss) { ut32 i; if (ss) { for (i = 0; i < ss->length; i++) { r_pkcs7_free_signerinfo (ss->elements[i]); ss->elements[i] = NULL; } R_FREE (ss->elements); // Used internally pkcs #7, so it shouldn't free ss. } } static bool r_pkcs7_parse_signeddata(RPKCS7SignedData *sd, RASN1Object *object) { ut32 shift = 3; if (!sd || !object || object->list.length < 4) { return false; } memset (sd, 0, sizeof (RPKCS7SignedData)); RASN1Object **elems = object->list.objects; // Following RFC sd->version = (ut32)elems[0]->sector[0]; r_pkcs7_parse_digestalgorithmidentifier (&sd->digestAlgorithms, elems[1]); r_pkcs7_parse_contentinfo (&sd->contentInfo, elems[2]); // Optional if (object->list.length > 3 && shift < object->list.length && elems[shift] && elems[shift]->klass == CLASS_CONTEXT && elems[shift]->tag == 0) { r_pkcs7_parse_extendedcertificatesandcertificates (&sd->certificates, elems[shift]); shift++; } // Optional if (object->list.length > 3 && shift < object->list.length && elems[shift] && elems[shift]->klass == CLASS_CONTEXT && elems[shift]->tag == 1) { r_pkcs7_parse_certificaterevocationlists (&sd->crls, elems[shift]); shift++; } if (shift < object->list.length) { r_pkcs7_parse_signerinfos (&sd->signerinfos, elems[shift]); } return true; } static void r_pkcs7_free_signeddata(RPKCS7SignedData *sd) { if (sd) { r_pkcs7_free_digestalgorithmidentifier (&sd->digestAlgorithms); r_pkcs7_free_contentinfo (&sd->contentInfo); r_pkcs7_free_extendedcertificatesandcertificates (&sd->certificates); r_pkcs7_free_certificaterevocationlists (&sd->crls); r_pkcs7_free_signerinfos (&sd->signerinfos); // Used internally pkcs #7, so it shouldn't free sd. } } R_API RCMS *r_pkcs7_parse_cms(const ut8 *buffer, ut32 length) { if (!buffer || !length) { return NULL; } RCMS *container = R_NEW0 (RCMS); if (!container) { return NULL; } RASN1Object *object = r_asn1_object_parse (buffer, buffer, length, 0); if (!object || object->list.length < 2 || !object->list.objects || !object->list.objects[0] || !object->list.objects[1] || object->list.objects[1]->list.length < 1) { r_asn1_object_free (object); free (container); return NULL; } if (object->list.objects[0]) { container->contentType = r_asn1_stringify_oid (object->list.objects[0]->sector, object->list.objects[0]->length); if (!container->contentType) { r_asn1_object_free (object); free (container); return NULL; } } if (object->list.objects[1]) { r_pkcs7_parse_signeddata (&container->signedData, object->list.objects[1]->list.objects[0]); } r_asn1_object_free (object); return container; } R_API void r_pkcs7_free_cms(RCMS *container) { if (container) { r_asn1_string_free (container->contentType); r_pkcs7_free_signeddata (&container->signedData); free (container); } } static RPKCS7Attribute *r_pkcs7_parse_attribute(RASN1Object *object) { RPKCS7Attribute *attribute; if (!object || object->list.length < 1) { return NULL; } attribute = R_NEW0 (RPKCS7Attribute); if (!attribute) { return NULL; } if (object->list.objects[0]) { attribute->oid = r_asn1_stringify_oid (object->list.objects[0]->sector, object->list.objects[0]->length); } if (object->list.length == 2) { RASN1Object *obj1 = object->list.objects[1]; if (obj1) { attribute->data = r_asn1_create_binary (obj1->sector, obj1->length); } } return attribute; } static bool r_pkcs7_parse_attributes(RPKCS7Attributes *attributes, RASN1Object *object) { ut32 i; if (!attributes || !object || !object->list.length) { return false; } attributes->length = object->list.length; if (attributes->length > 0) { attributes->elements = R_NEWS0 (RPKCS7Attribute *, attributes->length); if (!attributes->elements) { attributes->length = 0; return false; } for (i = 0; i < object->list.length; i++) { attributes->elements[i] = r_pkcs7_parse_attribute (object->list.objects[i]); } } return true; } #if 0 // XXX: unused static void r_pkcs7_signerinfos_dump(RX509CertificateRevocationList *crl, const char* pad, RStrBuf *sb) { RASN1String *algo = NULL, *last = NULL, *next = NULL; ut32 i; char *pad2, *pad3; if (!crl) { return; } if (!pad) { pad = ""; } pad3 = r_str_newf ("%s ", pad); if (!pad3) return; pad2 = pad3 + 2; algo = crl->signature.algorithm; last = crl->lastUpdate; next = crl->nextUpdate; r_strbuf_appendf (sb, "%sCRL:\n%sSignature:\n%s%s\n%sIssuer\n", pad, pad2, pad3, algo ? algo->string : "", pad2); r_x509_name_dump (&crl->issuer, pad3, sb); r_strbuf_appendf (sb, "%sLast Update: %s\n%sNext Update: %s\n%sRevoked Certificates:\n", pad2, last ? last->string : "Missing", pad2, next ? next->string : "Missing", pad2); for (i = 0; i < crl->length; i++) { r_x509_crlentry_dump (crl->revokedCertificates[i], pad3, sb); } free (pad3); } #endif static void r_x509_signedinfo_dump(RPKCS7SignerInfo *si, const char *pad, RStrBuf *sb) { RASN1String *s = NULL; ut32 i; if (!si) { return; } if (!pad) { pad = ""; } char *pad3 = r_str_newf ("%s ", pad); if (!pad3) { return; } char *pad2 = pad3 + 2; r_strbuf_appendf (sb, "%sSignerInfo:\n%sVersion: v%u\n%sIssuer\n", pad, pad2, si->version + 1, pad2); r_x509_name_dump (&si->issuerAndSerialNumber.issuer, pad3, sb); RASN1Binary *o = si->issuerAndSerialNumber.serialNumber; if (o) { s = r_asn1_stringify_integer (o->binary, o->length); } r_strbuf_appendf (sb, "%sSerial Number:\n%s%s\n", pad2, pad3, s ? s->string : "Missing"); r_asn1_string_free (s); s = si->digestAlgorithm.algorithm; r_strbuf_appendf (sb, "%sDigest Algorithm:\n%s%s\n%sAuthenticated Attributes:\n", pad2, pad3, s ? s->string : "Missing", pad2); for (i = 0; i < si->authenticatedAttributes.length; i++) { RPKCS7Attribute *attr = si->authenticatedAttributes.elements[i]; if (!attr) { continue; } r_strbuf_appendf (sb, "%s%s: %u bytes\n", pad3, attr->oid ? attr->oid->string : "Missing", attr->data ? attr->data->length : 0); } s = si->digestEncryptionAlgorithm.algorithm; r_strbuf_appendf (sb, "%sDigest Encryption Algorithm\n%s%s\n", pad2, pad3, s ? s->string : "Missing"); // if ((o = si->encryptedDigest)) s = r_asn1_stringify_bytes (o->binary, o->length); // else s = NULL; // eprintf ("%sEncrypted Digest: %u bytes\n%s\n", pad2, o ? o->length : 0, s ? s->string : "Missing"); // r_asn1_string_free (s); r_strbuf_appendf (sb, "%sEncrypted Digest: %u bytes\n", pad2, o ? o->length : 0); r_strbuf_appendf (sb, "%sUnauthenticated Attributes:\n", pad2); for (i = 0; i < si->unauthenticatedAttributes.length; i++) { RPKCS7Attribute *attr = si->unauthenticatedAttributes.elements[i]; if (!attr) { continue; } o = attr->data; eprintf ("%s%s: %u bytes\n", pad3, attr->oid ? attr->oid->string : "Missing", o ? o->length : 0); } free (pad3); } R_API char *r_pkcs7_cms_tostring(RCMS *container) { ut32 i; if (!container) { return NULL; } RPKCS7SignedData *sd = &container->signedData; RStrBuf *sb = r_strbuf_new (""); r_strbuf_appendf (sb, "signedData\n Version: v%u\n Digest Algorithms:\n", sd->version); if (container->signedData.digestAlgorithms.elements) { for (i = 0; i < container->signedData.digestAlgorithms.length; i++) { if (container->signedData.digestAlgorithms.elements[i]) { RASN1String *s = container->signedData.digestAlgorithms.elements[i]->algorithm; r_strbuf_appendf (sb, " %s\n", s ? s->string : "Missing"); } } } r_strbuf_appendf (sb, " Certificates: %u\n", container->signedData.certificates.length); for (i = 0; i < container->signedData.certificates.length; i++) { r_x509_certificate_dump (container->signedData.certificates.elements[i], " ", sb); } for (i = 0; i < container->signedData.crls.length; i++) { char *res = r_x509_crl_tostring (container->signedData.crls.elements[i], " "); if (res) { r_strbuf_append (sb, res); free (res); } } r_strbuf_append (sb, " SignerInfos:\n"); if (container->signedData.signerinfos.elements) { for (i = 0; i < container->signedData.signerinfos.length; i++) { r_x509_signedinfo_dump (container->signedData.signerinfos.elements[i], " ", sb); } } return r_strbuf_drain (sb); } R_API void r_x509_signedinfo_json(PJ *pj, RPKCS7SignerInfo *si) { ut32 i; if (si) { pj_o (pj); pj_ki (pj, "Version", si->version + 1); pj_k (pj, "Issuer"); pj_o (pj); r_x509_name_json (pj, &si->issuerAndSerialNumber.issuer); pj_end (pj); if (si->issuerAndSerialNumber.serialNumber) { RASN1Binary *o = si->issuerAndSerialNumber.serialNumber; RASN1String *s = r_asn1_stringify_integer (o->binary, o->length); if (s) { pj_ks (pj, "SerialNumber", s->string); } r_asn1_string_free (s); } if (si->digestAlgorithm.algorithm) { pj_ks (pj, "DigestAlgorithm", si->digestAlgorithm.algorithm->string); } pj_k (pj, "AuthenticatedAttributes"); pj_a (pj); for (i = 0; i < si->authenticatedAttributes.length; i++) { RPKCS7Attribute *attr = si->authenticatedAttributes.elements[i]; if (!attr) { continue; } pj_o (pj); if (attr->oid) { pj_ks (pj, "oid", attr->oid->string); } if (attr->data) { pj_ki (pj, "length", attr->data->length); } pj_end (pj); } pj_end (pj); if (si->digestEncryptionAlgorithm.algorithm) { pj_ks (pj, "DigestEncryptionAlgorithm", si->digestEncryptionAlgorithm.algorithm->string); } if (si->encryptedDigest) { RASN1Binary *o = si->encryptedDigest; RASN1String *s = r_asn1_stringify_integer (o->binary, o->length); if (s) { pj_ks (pj, "EncryptedDigest", s->string); } r_asn1_string_free (s); } pj_k (pj, "UnauthenticatedAttributes"); pj_a (pj); for (i = 0; i < si->unauthenticatedAttributes.length; i++) { RPKCS7Attribute *attr = si->unauthenticatedAttributes.elements[i]; if (!attr) { continue; } pj_o (pj); if (attr->oid) { pj_ks (pj, "oid", attr->oid->string); } if (attr->data) { pj_ki (pj, "length", attr->data->length); } pj_end (pj); } pj_end (pj); pj_end (pj); } } R_API PJ *r_pkcs7_cms_json(RCMS *container) { PJ *pj = NULL; if (container) { ut32 i; pj = pj_new (); pj_o (pj); pj_kn (pj, "Version", container->signedData.version); if (container->signedData.digestAlgorithms.elements) { pj_k (pj, "DigestAlgorithms"); pj_a (pj); for (i = 0; i < container->signedData.digestAlgorithms.length; i++) { if (container->signedData.digestAlgorithms.elements[i]) { RASN1String *s = container->signedData.digestAlgorithms.elements[i]->algorithm; if (s) { pj_s (pj, s->string); } } } pj_end (pj); } pj_k (pj, "Certificates"); pj_a (pj); for (i = 0; i < container->signedData.certificates.length; i++) { r_x509_certificate_json (pj, container->signedData.certificates.elements[i]); } pj_end (pj); pj_k (pj, "CRL"); pj_a (pj); for (i = 0; i < container->signedData.crls.length; i++) { r_x509_crl_json (pj, container->signedData.crls.elements[i]); } pj_end (pj); pj_k (pj, "SignerInfos"); pj_a (pj); if (container->signedData.signerinfos.elements) { for (i = 0; i < container->signedData.signerinfos.length; i++) { r_x509_signedinfo_json (pj, container->signedData.signerinfos.elements[i]); } } pj_end (pj); pj_end (pj); } return pj; } static bool r_pkcs7_parse_spcdata(SpcAttributeTypeAndOptionalValue *data, RASN1Object *object) { if (!data || !object || object->list.length < 1 || !object->list.objects[0]) { return false; } data->type = r_asn1_stringify_oid (object->list.objects[0]->sector, object->list.objects[0]->length); if (!data->type) { return false; } RASN1Object *obj1 = object->list.objects[1]; if (object->list.length > 1) { if (obj1) { data->data = r_asn1_create_binary (obj1->sector, obj1->length); } } return true; } static bool r_pkcs7_parse_spcmessagedigest(SpcDigestInfo *messageDigest, RASN1Object *object) { if (!messageDigest || !object || object->list.length < 2 || !object->list.objects[0] || !object->list.objects[1]) { return false; } if (!r_x509_parse_algorithmidentifier (&messageDigest->digestAlgorithm, object->list.objects[0])) { return false; } RASN1Object *obj1 = object->list.objects[1]; messageDigest->digest = r_asn1_create_binary (obj1->sector, obj1->length); return true; } R_API SpcIndirectDataContent *r_pkcs7_parse_spcinfo(RCMS *cms) { r_return_val_if_fail (cms, NULL); RASN1String *type = cms->signedData.contentInfo.contentType; if (type && strcmp (type->string, "spcIndirectDataContext")) { return NULL; } SpcIndirectDataContent *spcinfo = R_NEW0 (SpcIndirectDataContent); if (!spcinfo) { return NULL; } RASN1Binary *content = cms->signedData.contentInfo.content; if (!content) { free (spcinfo); return NULL; } RASN1Object *object = r_asn1_object_parse (content->binary, content->binary, content->length, 0); if (!object || object->list.length < 2 || !object->list.objects || !object->list.objects[0] || !object->list.objects[1]) { R_FREE (spcinfo); goto beach; } if (object->list.objects[0]) { if (!r_pkcs7_parse_spcdata (&spcinfo->data, object->list.objects[0])) { R_FREE (spcinfo); goto beach; } } if (object->list.objects[1]) { if (!r_pkcs7_parse_spcmessagedigest (&spcinfo->messageDigest, object->list.objects[1])) { R_FREE (spcinfo); goto beach; } } beach: r_asn1_object_free (object); return spcinfo; } static void r_pkcs7_free_spcdata(SpcAttributeTypeAndOptionalValue *data) { if (data) { r_asn1_string_free (data->type); r_asn1_binary_free (data->data); } } static void r_pkcs7_free_spcmessagedigest(SpcDigestInfo *messageDigest) { if (messageDigest) { r_asn1_binary_free (messageDigest->digest); r_x509_free_algorithmidentifier (&messageDigest->digestAlgorithm); } } R_API void r_pkcs7_free_spcinfo(SpcIndirectDataContent *spcinfo) { if (spcinfo) { r_pkcs7_free_spcdata (&spcinfo->data); r_pkcs7_free_spcmessagedigest (&spcinfo->messageDigest); } }