static AJ_Status DecodeCertificateTime(X509Validity* validity, DER_Element* der) { AJ_Status status; DER_Element time; uint8_t fmt; memset(validity, 0, sizeof (X509Validity)); if (!der->size) { return AJ_ERR_SECURITY; } fmt = *der->data; switch (fmt) { case ASN_UTC_TIME: status = AJ_ASN1DecodeElement(der, ASN_UTC_TIME, &time); if (AJ_OK != status) { return status; } validity->from = AJ_DecodeTime((char*) time.data, "%y%m%d%H%M%SZ"); break; case ASN_GEN_TIME: status = AJ_ASN1DecodeElement(der, ASN_GEN_TIME, &time); if (AJ_OK != status) { return status; } validity->from = AJ_DecodeTime((char*) time.data, "%Y%m%d%H%M%SZ"); break; default: return AJ_ERR_INVALID; } if (!der->size) { return AJ_ERR_SECURITY; } fmt = *der->data; switch (fmt) { case ASN_UTC_TIME: status = AJ_ASN1DecodeElement(der, ASN_UTC_TIME, &time); if (AJ_OK != status) { return status; } validity->to = AJ_DecodeTime((char*) time.data, "%y%m%d%H%M%SZ"); break; case ASN_GEN_TIME: status = AJ_ASN1DecodeElement(der, ASN_GEN_TIME, &time); if (AJ_OK != status) { return status; } validity->to = AJ_DecodeTime((char*) time.data, "%Y%m%d%H%M%SZ"); break; default: return AJ_ERR_INVALID; } return status; }
static AJ_Status DecodeCertificateSig(DER_Element* der, ecc_signature* signature) { AJ_Status status; DER_Element seq; DER_Element int1; DER_Element int2; uint8_t tags[] = { ASN_INTEGER, ASN_INTEGER }; status = AJ_ASN1DecodeElement(der, ASN_SEQ, &seq); if (AJ_OK != status) { return status; } status = AJ_ASN1DecodeElements(&seq, tags, sizeof (tags), &int1, &int2); if (AJ_OK != status) { return status; } /* * Skip over unused bits. */ if ((0 < int1.size) && (0 == *int1.data)) { int1.data++; int1.size--; } if ((0 < int2.size) && (0 == *int2.data)) { int2.data++; int2.size--; } memset(signature, 0, sizeof (ecc_signature)); AJ_BigvalDecode(int1.data, &signature->r, int1.size); AJ_BigvalDecode(int2.data, &signature->s, int2.size); return status; }
AJ_Status AJ_X509DecodeCertificateDER(X509Certificate* certificate, DER_Element* der) { AJ_Status status; DER_Element seq; DER_Element tbs; DER_Element tmp; DER_Element oid; DER_Element sig; const uint8_t tags1[] = { ASN_SEQ }; const uint8_t tags2[] = { ASN_SEQ, ASN_SEQ, ASN_BITS }; AJ_InfoPrintf(("AJ_X509DecodeCertificateDER(certificate=%p, der=%p)\n", certificate, der)); if ((NULL == certificate) || (NULL == der)) { return AJ_ERR_INVALID; } status = AJ_ASN1DecodeElements(der, tags1, sizeof (tags1), &seq); if (AJ_OK != status) { return status; } status = AJ_ASN1DecodeElements(&seq, tags2, sizeof (tags2), &tbs, &tmp, &sig); if (AJ_OK != status) { return status; } /* * The signed TBS includes the sequence and length fields. */ certificate->raw.data = tbs.data - 4; certificate->raw.size = tbs.size + 4; status = DecodeCertificateTBS(&certificate->tbs, &tbs); if (AJ_OK != status) { return status; } /* * We only accept ECDSA-SHA256 signed certificates at the moment. */ status = AJ_ASN1DecodeElement(&tmp, ASN_OID, &oid); if (AJ_OK != status) { return status; } if (!CompareOID(&oid, OID_SIG_ECDSA_SHA256, sizeof (OID_SIG_ECDSA_SHA256))) { return AJ_ERR_INVALID; } /* * Remove the byte specifying unused bits, this should always be zero. */ if ((0 == sig.size) || (0 != *sig.data)) { return AJ_ERR_INVALID; } sig.data++; sig.size--; status = DecodeCertificateSig(&certificate->signature, &sig); return status; }
static AJ_Status DecodeCertificateName(X509DistinguishedName* dn, DER_Element* der) { AJ_Status status = AJ_OK; DER_Element set; DER_Element seq; DER_Element oid; DER_Element tmp; memset(dn, 0, sizeof (X509DistinguishedName)); while ((AJ_OK == status) && (der->size)) { status = AJ_ASN1DecodeElement(der, ASN_SET_OF, &set); if (AJ_OK != status) { return status; } status = AJ_ASN1DecodeElement(&set, ASN_SEQ, &seq); if (AJ_OK != status) { return status; } status = AJ_ASN1DecodeElement(&seq, ASN_OID, &oid); if (AJ_OK != status) { return status; } if (CompareOID(&oid, OID_DN_OU, sizeof (OID_DN_OU))) { // Only accept UTF8 strings status = AJ_ASN1DecodeElement(&seq, ASN_UTF8, &tmp); if (AJ_OK != status) { return status; } dn->ou.data = tmp.data; dn->ou.size = tmp.size; } else if (CompareOID(&oid, OID_DN_CN, sizeof (OID_DN_CN))) { // Only accept UTF8 strings status = AJ_ASN1DecodeElement(&seq, ASN_UTF8, &tmp); if (AJ_OK != status) { return status; } dn->cn.data = tmp.data; dn->cn.size = tmp.size; } } return status; }
static AJ_Status DecodeCertificateName(DER_Element* der, uint8_t type, AJ_GUID* ou, AJ_GUID* cn) { AJ_Status status = AJ_OK; DER_Element set; DER_Element seq; DER_Element oid; DER_Element tmp; while ((AJ_OK == status) && (der->size)) { status = AJ_ASN1DecodeElement(der, ASN_SET_OF, &set); if (AJ_OK != status) { return status; } status = AJ_ASN1DecodeElement(&set, ASN_SEQ, &seq); if (AJ_OK != status) { return status; } status = AJ_ASN1DecodeElement(&seq, ASN_OID, &oid); if (AJ_OK != status) { return status; } status = AJ_ASN1DecodeElement(&seq, ASN_UTF8, &tmp); if (AJ_OK != status) { return status; } if (CompareOID(&oid, OID_DN_OU, sizeof (OID_DN_OU))) { status = AJ_GUID_FromString(ou, (const char*) tmp.data); if (AJ_OK != status) { return status; } } else if (CompareOID(&oid, OID_DN_CN, sizeof (OID_DN_CN))) { status = AJ_GUID_FromString(cn, (const char*) tmp.data); if (AJ_OK != status) { return status; } } } return status; }
AJ_Status AJ_ASN1DecodeElements(DER_Element* der, uint8_t* tags, size_t len, ...) { AJ_Status status = AJ_OK; DER_Element* out; va_list argp; uint8_t tag; uint32_t tmp; AJ_InfoPrintf(("AJ_ASN1DecodeElements(der=%p, tags=%p, len=%zu)\n", der, tags, len)); if ((NULL == der) || (NULL == tags)) { return AJ_ERR_INVALID; } va_start(argp, len); while ((AJ_OK == status) && len && (der->size)) { tag = *tags++; if (ASN_CONTEXT_SPECIFIC == tag) { tmp = va_arg(argp, uint32_t); tag = (ASN_CONTEXT_SPECIFIC | tmp); } out = va_arg(argp, DER_Element*); len--; status = AJ_ASN1DecodeElement(der, tag, out); } va_end(argp); /* * Should all be consumed */ if (len || der->size) { status = AJ_ERR_INVALID; } return status; }
AJ_Status AJ_ASN1DecodeElements(DER_Element* der, const uint8_t* tags, size_t len, ...) { AJ_Status status = AJ_OK; DER_Element* out; va_list argp; uint8_t tag; uint32_t tmp; AJ_InfoPrintf(("AJ_ASN1DecodeElements(der=%p, tags=%p, len=%zu)\n", der, tags, len)); if ((NULL == der) || (NULL == tags)) { return AJ_ERR_INVALID; } va_start(argp, len); while ((AJ_OK == status) && len && (der->size)) { tag = *tags++; if (ASN_CONTEXT_SPECIFIC == tag) { tmp = va_arg(argp, uint32_t); tag = (ASN_CONTEXT_SPECIFIC | tmp); } out = va_arg(argp, DER_Element*); len--; status = AJ_ASN1DecodeElement(der, tag, out); } if (AJ_OK == status) { // If unset elements, fail if (len) { AJ_InfoPrintf(("AJ_ASN1DecodeElements(der=%p, tags=%p, len=%zu): Uninitialized elements\n", der, tags, len)); status = AJ_ERR_INVALID; } } va_end(argp); return status; }
static AJ_Status DecodeCertificateTBS(X509Certificate* certificate, DER_Element* tbs) { AJ_Status status; DER_Element ver; DER_Element oid; DER_Element iss; DER_Element utc; DER_Element sub; DER_Element pub; DER_Element ext; DER_Element tmp; DER_Element time1; DER_Element time2; uint8_t tags1[] = { ASN_CONTEXT_SPECIFIC, ASN_INTEGER, ASN_SEQ, ASN_SEQ, ASN_SEQ, ASN_SEQ, ASN_SEQ, ASN_CONTEXT_SPECIFIC }; uint8_t tags2[] = { ASN_UTC_TIME, ASN_UTC_TIME }; status = AJ_ASN1DecodeElements(tbs, tags1, sizeof (tags1), 0, &ver, &certificate->serial, &oid, &iss, &utc, &sub, &pub, 3, &ext); if (AJ_OK != status) { return status; } /* * We only accept X.509v3 certificates. */ status = AJ_ASN1DecodeElement(&ver, ASN_INTEGER, &tmp); if (AJ_OK != status) { return status; } if ((0x1 != tmp.size) || (0x2 != *tmp.data)) { return AJ_ERR_INVALID; } /* * We only accept ECDSA-SHA256 signed certificates at the moment. */ status = AJ_ASN1DecodeElement(&oid, ASN_OID, &tmp); if (AJ_OK != status) { return status; } if (!CompareOID(&tmp, OID_SIG_ECDSA_SHA256, sizeof (OID_SIG_ECDSA_SHA256))) { return AJ_ERR_INVALID; } status = DecodeCertificateName(&iss, 0, NULL, &certificate->issuer); if (AJ_OK != status) { return status; } status = AJ_ASN1DecodeElements(&utc, tags2, sizeof (tags2), &time1, &time2); if (AJ_OK != status) { return status; } status = DecodeCertificateName(&sub, 0, &certificate->guild, &certificate->subject); if (AJ_OK != status) { return status; } status = DecodeCertificatePub(&pub, &certificate->keyinfo); if (AJ_OK != status) { return status; } memset(certificate->digest, 0, SHA256_DIGEST_LENGTH); status = DecodeCertificateExt(certificate, &ext); return status; }
static AJ_Status DecodeCertificateExt(X509Certificate* certificate, DER_Element* der) { AJ_Status status; DER_Element tmp; DER_Element seq; DER_Element oid; DER_Element oct; uint8_t tags[] = { ASN_OID, ASN_OCTETS }; certificate->type = UNKNOWN_CERTIFICATE; status = AJ_ASN1DecodeElement(der, ASN_SEQ, &tmp); if (AJ_OK != status) { return status; } der->size = tmp.size; der->data = tmp.data; while ((AJ_OK == status) && (der->size)) { status = AJ_ASN1DecodeElement(der, ASN_SEQ, &seq); if (AJ_OK != status) { return status; } status = AJ_ASN1DecodeElements(&seq, tags, sizeof (tags), &oid, &oct); if (AJ_OK != status) { return status; } if (CompareOID(&oid, OID_BASIC_CONSTRAINTS, sizeof (OID_BASIC_CONSTRAINTS))) { status = AJ_ASN1DecodeElement(&oct, ASN_SEQ, &seq); if (AJ_OK != status) { return status; } status = AJ_ASN1DecodeElement(&seq, ASN_BOOLEAN, &tmp); if (AJ_OK != status) { return status; } } else if (CompareOID(&oid, OID_CUSTOM_CERT_TYPE, sizeof (OID_CUSTOM_CERT_TYPE))) { status = AJ_ASN1DecodeElement(&oct, ASN_SEQ, &seq); if (AJ_OK != status) { return status; } status = AJ_ASN1DecodeElement(&seq, ASN_INTEGER, &tmp); if (AJ_OK != status) { return status; } if (sizeof (uint32_t) < tmp.size) { return AJ_ERR_INVALID; } certificate->type = 0; while (tmp.size--) { certificate->type <<= 8; certificate->type |= *tmp.data++; } } else if (CompareOID(&oid, OID_SUB_ALTNAME, sizeof (OID_SUB_ALTNAME))) { certificate->alias.size = oct.size; certificate->alias.data = oct.data; } else if (CompareOID(&oid, OID_CUSTOM_DIGEST, sizeof (OID_CUSTOM_DIGEST))) { status = AJ_ASN1DecodeElement(&oct, ASN_SEQ, &seq); if (AJ_OK != status) { return status; } status = AJ_ASN1DecodeElements(&seq, tags, sizeof (tags), &oid, &oct); if (AJ_OK != status) { return status; } if (!CompareOID(&oid, OID_DIG_SHA256, sizeof (OID_DIG_SHA256))) { return AJ_ERR_INVALID; } if (SHA256_DIGEST_LENGTH != oct.size) { return AJ_ERR_INVALID; } memcpy(certificate->digest, oct.data, SHA256_DIGEST_LENGTH); } else { // Ignore extensions we don't know AJ_DumpBytes("UNKNOWN", oid.data, oid.size); AJ_DumpBytes("UNKNOWN", oct.data, oct.size); } } return status; }
static AJ_Status DecodeCertificateTBS(X509TbsCertificate* tbs, DER_Element* der) { AJ_Status status; DER_Element ver; DER_Element oid; DER_Element iss; DER_Element utc; DER_Element sub; DER_Element pub; DER_Element ext; DER_Element tmp; const uint8_t tags[] = { ASN_CONTEXT_SPECIFIC, ASN_INTEGER, ASN_SEQ, ASN_SEQ, ASN_SEQ, ASN_SEQ, ASN_SEQ, ASN_CONTEXT_SPECIFIC }; memset(tbs, 0, sizeof (X509TbsCertificate)); status = AJ_ASN1DecodeElements(der, tags, sizeof (tags), 0, &ver, &tbs->serial, &oid, &iss, &utc, &sub, &pub, 3, &ext); if (AJ_OK != status) { return status; } /* * We only accept X.509v3 certificates. */ status = AJ_ASN1DecodeElement(&ver, ASN_INTEGER, &tmp); if (AJ_OK != status) { return status; } if ((0x1 != tmp.size) || (0x2 != *tmp.data)) { return AJ_ERR_INVALID; } /* * We only accept ECDSA-SHA256 signed certificates at the moment. */ status = AJ_ASN1DecodeElement(&oid, ASN_OID, &tmp); if (AJ_OK != status) { return status; } if (!CompareOID(&tmp, OID_SIG_ECDSA_SHA256, sizeof (OID_SIG_ECDSA_SHA256))) { return AJ_ERR_INVALID; } status = DecodeCertificateName(&tbs->issuer, &iss); if (AJ_OK != status) { return status; } status = DecodeCertificateTime(&tbs->validity, &utc); if (AJ_OK != status) { return status; } status = DecodeCertificateName(&tbs->subject, &sub); if (AJ_OK != status) { return status; } status = DecodeCertificatePub(&tbs->publickey, &pub); if (AJ_OK != status) { return status; } status = DecodeCertificateExt(&tbs->extensions, &ext); if (AJ_OK != status) { return status; } return status; }
static AJ_Status DecodeCertificateExt(X509Extensions* extensions, DER_Element* der) { AJ_Status status; DER_Element tmp; DER_Element seq; DER_Element savedSeq; DER_Element boolVal; DER_Element intVal; DER_Element oid; DER_Element oct; const uint8_t tags[] = { ASN_OID, ASN_OCTETS }; const uint8_t tagsWithCritical[] = { ASN_OID, ASN_BOOLEAN, ASN_OCTETS }; const uint8_t tagsCAPathLen[] = { ASN_BOOLEAN, ASN_INTEGER }; memset(extensions, 0, sizeof (X509Extensions)); status = AJ_ASN1DecodeElement(der, ASN_SEQ, &tmp); if (AJ_OK != status) { return status; } der->size = tmp.size; der->data = tmp.data; while ((AJ_OK == status) && (der->size)) { status = AJ_ASN1DecodeElement(der, ASN_SEQ, &seq); if (AJ_OK != status) { return status; } savedSeq.size = seq.size; savedSeq.data = seq.data; status = AJ_ASN1DecodeElements(&seq, tagsWithCritical, sizeof (tagsWithCritical), &oid, &boolVal, &oct); if (AJ_OK != status) { status = AJ_ASN1DecodeElements(&savedSeq, tags, sizeof (tags), &oid, &oct); if (AJ_OK != status) { return status; } } if (CompareOID(&oid, OID_BASIC_CONSTRAINTS, sizeof (OID_BASIC_CONSTRAINTS))) { status = AJ_ASN1DecodeElement(&oct, ASN_SEQ, &seq); if (AJ_OK != status) { return status; } // Explicit boolean (non-empty sequence) if (seq.size) { savedSeq.size = seq.size; savedSeq.data = seq.data; status = AJ_ASN1DecodeElements(&seq, tagsCAPathLen, sizeof (tagsCAPathLen), &tmp, &intVal); if (AJ_OK != status) { status = AJ_ASN1DecodeElement(&savedSeq, ASN_BOOLEAN, &tmp); if (AJ_OK != status) { return status; } } if (tmp.size) { extensions->ca = *tmp.data; } } } } return status; }