diff --git a/certs/fpki-cert.der b/certs/fpki-cert.der new file mode 100644 index 000000000..2b88caf24 Binary files /dev/null and b/certs/fpki-cert.der differ diff --git a/certs/include.am b/certs/include.am index 78b432ef2..3ab8337a5 100644 --- a/certs/include.am +++ b/certs/include.am @@ -63,7 +63,8 @@ EXTRA_DIST += \ certs/csr.ext.der \ certs/entity-no-ca-bool-cert.pem \ certs/entity-no-ca-bool-key.pem \ - certs/x942dh2048.pem + certs/x942dh2048.pem \ + certs/fpki-cert.der EXTRA_DIST += \ certs/ca-key.der \ diff --git a/certs/renewcerts.sh b/certs/renewcerts.sh index ebef14a7f..208fc1a58 100755 --- a/certs/renewcerts.sh +++ b/certs/renewcerts.sh @@ -27,6 +27,7 @@ # client-relative-uri.pem # client-crl-dist.pem # entity-no-ca-bool-cert.pem +# fpki-cert.der # updates the following crls: # crl/cliCrl.pem # crl/crl.pem @@ -344,6 +345,20 @@ run_renewcerts(){ echo "End of section" echo "---------------------------------------------------------------------" ########################################################### + ########## update and sign fpki-cert.der ################ + ########################################################### + echo "Updating fpki-cert.der" + echo "" + #pipe the following arguments to openssl req... + echo -e "US\\nMontana\\nBozeman\\nwolfSSL\\nFPKI\\nwww.wolfssl.com\\ninfo@wolfssl.com\\n.\\n.\\n" | openssl req -new -key server-key.pem -config ./wolfssl.cnf -nodes > fpki-req.pem + check_result $? "Step 1" + + openssl x509 -req -in fpki-req.pem -extfile wolfssl.cnf -extensions fpki_ext -days 1000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out fpki-cert.der -outform DER + check_result $? "Step 2" + rm fpki-req.pem + echo "End of section" + echo "---------------------------------------------------------------------" + ########################################################### ########## update and sign server-cert.pem ################ ########################################################### echo "Updating server-cert.pem" diff --git a/certs/renewcerts/wolfssl.cnf b/certs/renewcerts/wolfssl.cnf index faea03f5d..af26cdc23 100644 --- a/certs/renewcerts/wolfssl.cnf +++ b/certs/renewcerts/wolfssl.cnf @@ -335,3 +335,40 @@ clock_precision_digits = 0 # (optional) ordering = yes # timestamps? tsa_name = yes # include? ess_cert_id_chain = no # include chain? + + +[fpki_ext] +basicConstraints = CA:FALSE,pathlen:0 +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid +keyUsage = critical, digitalSignature +extendedKeyUsage = critical, clientAuth, 1.3.6.1.4.1.311.20.2.2, 1.3.6.1.5.2.3.4, 1.3.6.1.5.5.7.3.21 +subjectAltName = @FASC_UUID_altname +certificatePolicies = 2.16.840.1.101.3.2.1.3.13, 2.16.840.1.101.3.2.1.3.40, 2.16.840.1.101.3.2.1.3.41, 2.16.840.1.101.3.2.1.3.45 +subjectDirectoryAttributes = ASN1:SEQUENCE:SubjDirAttr +policyConstraints = requireExplicitPolicy:0 +2.16.840.1.101.3.6.10.1 = ASN1:SEQUENCE:PIVCertExt + +# using example UUID from RFC4122 +[FASC_UUID_altname] +otherName.1 = 1.3.6.1.4.1.311.20.2.3;UTF8:facts@wolfssl.com +otherName.2 = 2.16.840.1.101.3.6.6;FORMAT:HEX,OCT:D1:38:10:D8:28:AF:2C:10:84:35:15:A1:68:58:28:AF:02:10:86:A2:84:E7:39:C3:EB +URI = urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6 + +[SubjDirAttr] +attribute = SEQUENCE:SDA_attr + +[SDA_attr] +type = OID:1.3.6.1.5.5.7.9.4 +values = SET:SDA_coc + +[SDA_coc] +value = PRINTABLESTRING:US + +[PIVCertExt] +attribute = SEQUENCE:PCE_attr + +[PCE_attr] +type = OID:2.16.840.1.101.3.6.9.1 +value = BOOLEAN:true + diff --git a/configure.ac b/configure.ac index 244707955..9e1bc8d97 100644 --- a/configure.ac +++ b/configure.ac @@ -756,6 +756,9 @@ then # Store issuer name components when parsing certificates. AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_HAVE_ISSUER_NAMES" + + # Certificate extensions and alt. names for FPKI use + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_SUBJ_DIR_ATTR -DWOLFSSL_FPKI -DWOLFSSL_SUBJ_INFO_ACC" fi diff --git a/src/x509.c b/src/x509.c index d0783d787..1e6ff1e2c 100644 --- a/src/x509.c +++ b/src/x509.c @@ -2201,7 +2201,7 @@ void* wolfSSL_X509_get_ext_d2i(const WOLFSSL_X509* x509, int nid, int* c, WOLFSSL_MSG("Private Key Usage Period extension not supported"); break; - case SUBJECT_INFO_ACCESS: + case SUBJ_INFO_ACC_OID: WOLFSSL_MSG("Subject Info Access extension not supported"); break; diff --git a/tests/api.c b/tests/api.c index be740d343..c70c45e20 100644 --- a/tests/api.c +++ b/tests/api.c @@ -2261,6 +2261,44 @@ static void test_wolfSSL_CertManagerNameConstraint5(void) #endif } +static void test_wolfSSL_FPKI(void) +{ +#if defined(WOLFSSL_FPKI) && !defined(NO_FILESYSTEM) + XFILE f; + const char* fpkiCert = "./certs/fpki-cert.der"; + DecodedCert cert; + byte buf[4096]; + byte* uuid; + byte* fascn; + word32 fascnSz; + word32 uuidSz; + int bytes; + + printf(testingFmt, "test_wolfSSL_FPKI"); + f = XFOPEN(fpkiCert, "rb"); + AssertTrue((f != XBADFILE)); + bytes = (int)XFREAD(buf, 1, sizeof(buf), f); + XFCLOSE(f); + + wc_InitDecodedCert(&cert, buf, bytes, NULL); + AssertIntEQ(wc_ParseCert(&cert, CERT_TYPE, 0, NULL), 0); + AssertIntEQ(wc_GetFASCNFromCert(&cert, NULL, &fascnSz), LENGTH_ONLY_E) ; + fascn = (byte*)XMALLOC(fascnSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + AssertNotNull(fascn); + AssertIntEQ(wc_GetFASCNFromCert(&cert, fascn, &fascnSz), 0); + XFREE(fascn, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + AssertIntEQ(wc_GetUUIDFromCert(&cert, NULL, &uuidSz), LENGTH_ONLY_E); + uuid = (byte*)XMALLOC(uuidSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + AssertNotNull(uuid); + AssertIntEQ(wc_GetUUIDFromCert(&cert, uuid, &uuidSz), 0); + XFREE(uuid, NULL, DYNAMIC_TYPE_TMP_BUFFER); + wc_FreeDecodedCert(&cert); + + printf(resultFmt, passed); +#endif +} + static void test_wolfSSL_CertManagerCRL(void) { #if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && defined(HAVE_CRL) && \ @@ -8979,14 +9017,15 @@ static void test_wolfSSL_URI(void) x509 = wolfSSL_X509_load_certificate_file(uri, WOLFSSL_FILETYPE_PEM); AssertNotNull(x509); - wolfSSL_FreeX509(x509); x509 = wolfSSL_X509_load_certificate_file(badUri, WOLFSSL_FILETYPE_PEM); -#if !defined(IGNORE_NAME_CONSTRAINTS) && !defined(WOLFSSL_NO_ASN_STRICT) +#if !defined(IGNORE_NAME_CONSTRAINTS) && !defined(WOLFSSL_NO_ASN_STRICT) \ + && !defined(WOLFSSL_FPKI) AssertNull(x509); #else AssertNotNull(x509); + wolfSSL_FreeX509(x509); #endif printf(resultFmt, passed); @@ -55524,6 +55563,7 @@ void ApiTest(void) test_wolfSSL_CertManagerNameConstraint3(); test_wolfSSL_CertManagerNameConstraint4(); test_wolfSSL_CertManagerNameConstraint5(); + test_wolfSSL_FPKI(); test_wolfSSL_CertManagerCRL(); test_wolfSSL_CTX_load_verify_locations_ex(); test_wolfSSL_CTX_load_verify_buffer_ex(); diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index f1ca492a8..8d43acfe8 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -80,6 +80,10 @@ ASN Options: extensions * WOLFSSL_HAVE_ISSUER_NAMES: Store pointers to issuer name components and their lengths and encodings. + * WOLFSSL_SUBJ_DIR_ATTR: Enable support for SubjectDirectoryAttributes + extension. + * WOLFSSL_SUBJ_INFO_ACC: Enable support for SubjectInfoAccess extension. + * WOLFSSL_FPKI: Enable support for FPKI (Federal PKI) extensions. */ #ifndef NO_ASN @@ -4058,13 +4062,33 @@ static const byte extExtKeyUsageOid[] = {85, 29, 37}; #ifdef HAVE_CRL static const byte extCrlNumberOid[] = {85, 29, 20}; #endif +#ifdef WOLFSSL_SUBJ_DIR_ATTR + static const byte extSubjDirAttrOid[] = {85, 29, 9}; +#endif +#ifdef WOLFSSL_SUBJ_INFO_ACC + static const byte extSubjInfoAccessOid[] = {43, 6, 1, 5, 5, 7, 1, 11}; +#endif /* certAuthInfoType */ static const byte extAuthInfoOcspOid[] = {43, 6, 1, 5, 5, 7, 48, 1}; static const byte extAuthInfoCaIssuerOid[] = {43, 6, 1, 5, 5, 7, 48, 2}; +#ifdef WOLFSSL_SUBJ_INFO_ACC + static const byte extAuthInfoCaRespOid[] = {43, 6, 1, 5, 5, 7, 48, 5}; +#endif /* WOLFSSL_SUBJ_INFO_ACC */ /* certPolicyType */ static const byte extCertPolicyAnyOid[] = {85, 29, 32, 0}; +#ifdef WOLFSSL_FPKI +#define CERT_POLICY_TYPE_OID_BASE(num) {96, 134, 72, 1, 101, 3, 2, 1, 3, num} + static const byte extCertPolicyFpkiCommonAuthOid[] = + CERT_POLICY_TYPE_OID_BASE(13); + static const byte extCertPolicyFpkiPivAuthOid[] = + CERT_POLICY_TYPE_OID_BASE(40); + static const byte extCertPolicyFpkiPivAuthHwOid[] = + CERT_POLICY_TYPE_OID_BASE(41); + static const byte extCertPolicyFpkiPiviAuthOid[] = + CERT_POLICY_TYPE_OID_BASE(45); +#endif /* WOLFSSL_FPKI */ /* certAltNameType */ static const byte extAltNamesHwNameOid[] = {43, 6, 1, 5, 5, 7, 8, 4}; @@ -4077,6 +4101,25 @@ static const byte extExtKeyUsageCodeSigningOid[] = {43, 6, 1, 5, 5, 7, 3, 3}; static const byte extExtKeyUsageEmailProtectOid[] = {43, 6, 1, 5, 5, 7, 3, 4}; static const byte extExtKeyUsageTimestampOid[] = {43, 6, 1, 5, 5, 7, 3, 8}; static const byte extExtKeyUsageOcspSignOid[] = {43, 6, 1, 5, 5, 7, 3, 9}; +#ifdef WOLFSSL_WOLFSSH +#define EXT_KEY_USAGE_OID_BASE(num) {43, 6, 1, 5, 5, 7, 3, num} + static const byte extExtKeyUsageSshClientAuthOid[] = + EXT_KEY_USAGE_OID_BASE(21); + static const byte extExtKeyUsageSshMSCLOid[] = + {43, 6, 1, 4, 1, 130, 55, 20, 2, 2}; + static const byte extExtKeyUsageSshKpClientAuthOid[] = + {43, 6, 1, 5, 2, 3, 4}; +#endif /* WOLFSSL_WOLFSSH */ + +#ifdef WOLFSSL_SUBJ_DIR_ATTR +#define SUBJ_DIR_ATTR_TYPE_OID_BASE(num) {43, 6, 1, 5, 5, 7, 9, num} + static const byte extSubjDirAttrDobOid[] = SUBJ_DIR_ATTR_TYPE_OID_BASE(1); + static const byte extSubjDirAttrPobOid[] = SUBJ_DIR_ATTR_TYPE_OID_BASE(2); + static const byte extSubjDirAttrGenderOid[] = + SUBJ_DIR_ATTR_TYPE_OID_BASE(3); + static const byte extSubjDirAttrCocOid[] = SUBJ_DIR_ATTR_TYPE_OID_BASE(4); + static const byte extSubjDirAttrCorOid[] = SUBJ_DIR_ATTR_TYPE_OID_BASE(5); +#endif #if defined(WOLFSSL_CERT_REQ) || defined(WOLFSSL_CERT_GEN) || \ defined(WOLFSSL_ASN_TEMPLATE) || defined(OPENSSL_EXTRA) || \ @@ -4636,6 +4679,18 @@ const byte* OidFromId(word32 id, word32 type, word32* oidSz) oid = ocspNoCheckOid; *oidSz = sizeof(ocspNoCheckOid); break; + #endif + #ifdef WOLFSSL_SUBJ_DIR_ATTR + case SUBJ_DIR_ATTR_OID: + oid = extSubjDirAttrOid; + *oidSz = sizeof(extSubjDirAttrOid); + break; + #endif + #ifdef WOLFSSL_SUBJ_INFO_ACC + case SUBJ_INFO_ACC_OID: + oid = extSubjInfoAccessOid; + *oidSz = sizeof(extSubjInfoAccessOid); + break; #endif default: break; @@ -4669,6 +4724,11 @@ const byte* OidFromId(word32 id, word32 type, word32* oidSz) oid = extAuthInfoCaIssuerOid; *oidSz = sizeof(extAuthInfoCaIssuerOid); break; + #ifdef WOLFSSL_SUBJ_INFO_ACC + case AIA_CA_REPO_OID: + oid = extAuthInfoCaRespOid; + *oidSz = sizeof(extAuthInfoCaRespOid); + #endif /* WOLFSSL_SUBJ_INFO_ACC */ default: break; } @@ -4680,6 +4740,24 @@ const byte* OidFromId(word32 id, word32 type, word32* oidSz) oid = extCertPolicyAnyOid; *oidSz = sizeof(extCertPolicyAnyOid); break; + #if defined(WOLFSSL_FPKI) + case CP_FPKI_COMMON_AUTH_OID: + oid = extCertPolicyFpkiCommonAuthOid; + *oidSz = sizeof(extCertPolicyFpkiCommonAuthOid); + break; + case CP_FPKI_PIV_AUTH_OID: + oid = extCertPolicyFpkiPivAuthOid; + *oidSz = sizeof(extCertPolicyFpkiPivAuthOid); + break; + case CP_FPKI_PIV_AUTH_HW_OID: /* collision with AES256CBCb */ + oid = extCertPolicyFpkiPivAuthHwOid; + *oidSz = sizeof(extCertPolicyFpkiPivAuthHwOid); + break; + case CP_FPKI_PIVI_AUTH_OID: + oid = extCertPolicyFpkiPiviAuthOid; + *oidSz = sizeof(extCertPolicyFpkiPiviAuthOid); + break; + #endif /* WOLFSSL_FPKI */ default: break; } @@ -4726,6 +4804,20 @@ const byte* OidFromId(word32 id, word32 type, word32* oidSz) oid = extExtKeyUsageOcspSignOid; *oidSz = sizeof(extExtKeyUsageOcspSignOid); break; + #ifdef WOLFSSL_WOLFSSH + case EKU_SSH_CLIENT_AUTH_OID: + oid = extExtKeyUsageSshClientAuthOid; + *oidSz = sizeof(extExtKeyUsageSshClientAuthOid); + break; + case EKU_SSH_MSCL_OID: + oid = extExtKeyUsageSshMSCLOid; + *oidSz = sizeof(extExtKeyUsageSshMSCLOid); + break; + case EKU_SSH_KP_CLIENT_AUTH_OID: + oid = extExtKeyUsageSshKpClientAuthOid; + *oidSz = sizeof(extExtKeyUsageSshKpClientAuthOid); + break; + #endif /* WOLFSSL_WOLFSSH */ default: break; } @@ -4967,6 +5059,34 @@ const byte* OidFromId(word32 id, word32 type, word32* oidSz) } break; #endif +#ifdef WOLFSSL_SUBJ_DIR_ATTR + case oidSubjDirAttrType: + switch (id) { + case SDA_DOB_OID: + oid = extSubjDirAttrDobOid; + *oidSz = sizeof(extSubjDirAttrDobOid); + break; + case SDA_POB_OID: + oid = extSubjDirAttrPobOid; + *oidSz = sizeof(extSubjDirAttrPobOid); + break; + case SDA_GENDER_OID: + oid = extSubjDirAttrGenderOid; + *oidSz = sizeof(extSubjDirAttrGenderOid); + break; + case SDA_COC_OID: + oid = extSubjDirAttrCocOid; + *oidSz = sizeof(extSubjDirAttrCocOid); + break; + case SDA_COR_OID: + oid = extSubjDirAttrCorOid; + *oidSz = sizeof(extSubjDirAttrCorOid); + break; + default: + break; + } + break; +#endif /* WOLFSSL_SUBJ_DIR_ATTR */ case oidIgnoreType: default: break; @@ -5290,6 +5410,22 @@ static int GetOID(const byte* input, word32* inOutIdx, word32* oid, /* Get the OID data for the id-type. */ checkOid = OidFromId(*oid, oidType, &checkOidSz); + #if defined(WOLFSSL_FPKI) + /* Handle OID sum collision of + AES256CBCb (454) 2.16.840.1.101.3.4.1.42 + CP_FPKI_PIV_AUTH_HW_OID (454) 2.16.840.1.101.3.2.1.3.41 + */ + #if defined(HAVE_AES_CBC) && defined(WOLFSSL_AES_256) + if ((actualOidSz == (word32)sizeof(blkAes256CbcOid)) && + (XMEMCMP(actualOid, blkAes256CbcOid, + sizeof(blkAes256CbcOid)) == 0)) { + + checkOid = blkAes256CbcOid; + checkOidSz = sizeof(blkAes256CbcOid); + } + #endif /* HAVE_AES_CBC */ + #endif /* WOLFSSL_FPKI */ + #ifdef ASN_DUMP_OID /* Dump out the data for debug. */ ret = DumpOID(actualOid, actualOidSz, *oid, oidType); @@ -11002,6 +11138,40 @@ static int GenerateDNSEntryIPString(DNS_entry* entry, void* heap) #ifdef WOLFSSL_ASN_TEMPLATE #if defined(WOLFSSL_CERT_GEN) || \ (!defined(NO_CERTS) && !defined(IGNORE_NAME_CONSTRAINTS)) + +/* Adds a DNS entry to a list of DNS entries + * + * @param [in, out] lst Linked list of DNS name entries. + * @param [in] entry Entry to add to the list + * @return 0 on success. + */ +static int AddDNSEntryToList(DNS_entry** lst, DNS_entry* entry) +{ +#if defined(OPENSSL_EXTRA) && !defined(WOLFSSL_ALT_NAMES_NO_REV) + entry->next = NULL; + if (*lst == NULL) { + /* First on list */ + *lst = entry; + } + else { + DNS_entry* temp = *lst; + + /* Find end */ + for (; (temp->next != NULL); temp = temp->next); + + /* Add to end */ + temp->next = entry; + } +#else + /* Prepend entry to linked list. */ + entry->next = *lst; + *lst = entry; +#endif + + return 0; +} + + /* Allocate a DNS entry and set the fields. * * @param [in] cert Certificate object. @@ -11055,26 +11225,7 @@ static int SetDNSEntry(DecodedCert* cert, const char* str, int strLen, } if (ret == 0) { - #if defined(OPENSSL_EXTRA) && !defined(WOLFSSL_ALT_NAMES_NO_REV) - dnsEntry->next = NULL; - if (*entries == NULL) { - /* First on list */ - *entries = dnsEntry; - } - else { - DNS_entry* temp = *entries; - - /* Find end */ - for (; (temp->next != NULL); temp = temp->next); - - /* Add to end */ - temp->next = dnsEntry; - } - #else - /* Prepend entry to linked list. */ - dnsEntry->next = *entries; - *entries = dnsEntry; - #endif + ret = AddDNSEntryToList(entries, dnsEntry); } return ret; @@ -14501,22 +14652,32 @@ static void AddAltName(DecodedCert* cert, DNS_entry* dnsEntry) #endif #ifdef WOLFSSL_ASN_TEMPLATE -#ifdef WOLFSSL_SEP +#if defined(WOLFSSL_SEP) || defined(WOLFSSL_FPKI) /* ASN.1 template for OtherName of an X.509 certificate. * X.509: RFC 5280, 4.2.1.6 - OtherName (without implicit outer SEQUENCE). * HW Name: RFC 4108, 5 - Hardware Module Name * Only support HW Name where the type is a HW serial number. + * + * Other Names handled for FPKI (Federal PKI) use: + * UPN (Universal Principal Name), a non-standard Other Name + * (RFC3280 sec 4.2.1.7). Often used with FIPS 201 smartcard login. + * FASC-N (Federal Agency Smart Credential Number), defined in the document + * fpki-x509-cert-policy-common.pdf. Used for a smart card ID. */ static const ASNItem otherNameASN[] = { /* TYPEID */ { 0, ASN_OBJECT_ID, 0, 0, 0 }, -/* VALUE */ { 0, ASN_CONTEXT_SPECIFIC | ASN_OTHERNAME_VALUE, 1, 0, 0 }, -/* HWN_SEQ */ { 1, ASN_SEQUENCE, 1, 0, 0 }, +/* VALUE */ { 0, ASN_CONTEXT_SPECIFIC | ASN_OTHERNAME_VALUE, 1, 1, 0 }, +/* UPN */ { 1, ASN_UTF8STRING, 0, 0, 2 }, +/* FASC-N */ { 1, ASN_OCTET_STRING, 0, 0, 2 }, +/* HWN_SEQ */ { 1, ASN_SEQUENCE, 1, 0, 2 }, /* HWN_TYPE */ { 2, ASN_OBJECT_ID, 0, 0, 0 }, /* HWN_NUM */ { 2, ASN_OCTET_STRING, 0, 0, 0 } }; enum { OTHERNAMEASN_IDX_TYPEID = 0, OTHERNAMEASN_IDX_VALUE, + OTHERNAMEASN_IDX_UPN, + OTHERNAMEASN_IDX_FASCN, OTHERNAMEASN_IDX_HWN_SEQ, OTHERNAMEASN_IDX_HWN_TYPE, OTHERNAMEASN_IDX_HWN_NUM, @@ -14525,57 +14686,21 @@ enum { /* Number of items in ASN.1 template for OtherName of an X.509 certificate. */ #define otherNameASN_Length (sizeof(otherNameASN) / sizeof(ASNItem)) -/* Decode data with OtherName format from after implicit SEQUENCE. - * - * @param [in, out] cert Certificate object. - * @param [in] input Buffer containing encoded OtherName. - * @param [in, out] inOutIdx On in, the index of the start of the OtherName. - * On out, index after OtherName. - * @param [in] maxIdx Maximum index of data in buffer. - * @return 0 on success. - * @return MEMORY_E on dynamic memory allocation failure. - * @return ASN_PARSE_E when BER encoded data does not match ASN.1 items or - * is invalid. - * @return ASN_PARSE_E when OID does is not HW Name. - * @return ASN_UNKNOWN_OID_E when the OID cannot be verified. - * @return BUFFER_E when data in buffer is too small. - */ -static int DecodeOtherName(DecodedCert* cert, const byte* input, - word32* inOutIdx, word32 maxIdx) +#ifdef WOLFSSL_SEP +static int DecodeSEP(ASNGetData* dataASN, DecodedCert* cert) { - DECL_ASNGETDATA(dataASN, otherNameASN_Length); int ret = 0; word32 oidLen, serialLen; - CALLOC_ASNGETDATA(dataASN, otherNameASN_Length, ret, cert->heap); + oidLen = dataASN[OTHERNAMEASN_IDX_HWN_TYPE].data.oid.length; + serialLen = dataASN[OTHERNAMEASN_IDX_HWN_NUM].data.ref.length; - if (ret == 0) { - /* Check the first OID is a recognized Alt Cert Name type. */ - GetASN_OID(&dataASN[OTHERNAMEASN_IDX_TYPEID], oidCertAltNameType); - /* Only support HW serial number. */ - GetASN_OID(&dataASN[OTHERNAMEASN_IDX_HWN_TYPE], oidIgnoreType); - /* Parse OtherName. */ - ret = GetASN_Items(otherNameASN, dataASN, otherNameASN_Length, 1, input, - inOutIdx, maxIdx); - } - if (ret == 0) { - /* Ensure expected OID. */ - if (dataASN[OTHERNAMEASN_IDX_TYPEID].data.oid.sum != HW_NAME_OID) { - WOLFSSL_MSG("\tunsupported OID"); - ret = ASN_PARSE_E; - } - } + /* Allocate space for HW type OID. */ + cert->hwType = (byte*)XMALLOC(oidLen, cert->heap, + DYNAMIC_TYPE_X509_EXT); + if (cert->hwType == NULL) + ret = MEMORY_E; - if (ret == 0) { - oidLen = dataASN[OTHERNAMEASN_IDX_HWN_TYPE].data.oid.length; - serialLen = dataASN[OTHERNAMEASN_IDX_HWN_NUM].data.ref.length; - - /* Allocate space for HW type OID. */ - cert->hwType = (byte*)XMALLOC(oidLen, cert->heap, - DYNAMIC_TYPE_X509_EXT); - if (cert->hwType == NULL) - ret = MEMORY_E; - } if (ret == 0) { /* Copy, into cert HW type OID */ XMEMCPY(cert->hwType, @@ -14598,11 +14723,99 @@ static int DecodeOtherName(DecodedCert* cert, const byte* input, cert->hwSerialNum[serialLen] = '\0'; cert->hwSerialNumSz = serialLen; } + return ret; +} +#endif /* WOLFSSL_SEP */ + +#ifdef WOLFSSL_FPKI +static int DecodeOtherHelper(ASNGetData* dataASN, DecodedCert* cert, int oid) +{ + DNS_entry* entry = NULL; + int ret = 0; + word32 bufLen = 0; + const char* buf = NULL; + + switch (oid) { + case FASCN_OID: + bufLen = dataASN[OTHERNAMEASN_IDX_FASCN].data.ref.length; + buf = (const char*)dataASN[OTHERNAMEASN_IDX_FASCN].data.ref.data; + break; + case UPN_OID: + bufLen = dataASN[OTHERNAMEASN_IDX_UPN].data.ref.length; + buf = (const char*)dataASN[OTHERNAMEASN_IDX_UPN].data.ref.data; + break; + default: + ret = ASN_UNKNOWN_OID_E; + } + + if (ret == 0) { + ret = SetDNSEntry(cert, buf, bufLen, ASN_OTHER_TYPE, &entry); + if (ret == 0) { + entry->oidSum = oid; + AddDNSEntryToList(&cert->altNames, entry); + } + } + return ret; +} +#endif /* WOLFSSL_FPKI */ + +/* Decode data with OtherName format from after implicit SEQUENCE. + * + * @param [in, out] cert Certificate object. + * @param [in] input Buffer containing encoded OtherName. + * @param [in, out] inOutIdx On in, the index of the start of the OtherName. + * On out, index after OtherName. + * @param [in] maxIdx Maximum index of data in buffer. + * @return 0 on success. + * @return MEMORY_E on dynamic memory allocation failure. + * @return ASN_PARSE_E when BER encoded data does not match ASN.1 items or + * is invalid. + * @return ASN_PARSE_E when OID does is not HW Name. + * @return ASN_UNKNOWN_OID_E when the OID cannot be verified. + * @return BUFFER_E when data in buffer is too small. + */ +static int DecodeOtherName(DecodedCert* cert, const byte* input, + word32* inOutIdx, word32 maxIdx) +{ + DECL_ASNGETDATA(dataASN, otherNameASN_Length); + int ret = 0; + + CALLOC_ASNGETDATA(dataASN, otherNameASN_Length, ret, cert->heap); + + if (ret == 0) { + /* Check the first OID is a recognized Alt Cert Name type. */ + GetASN_OID(&dataASN[OTHERNAMEASN_IDX_TYPEID], oidCertAltNameType); + /* Parse OtherName. */ + ret = GetASN_Items(otherNameASN, dataASN, otherNameASN_Length, 1, input, + inOutIdx, maxIdx); + } + if (ret == 0) { + /* Ensure expected OID. */ + switch (dataASN[OTHERNAMEASN_IDX_TYPEID].data.oid.sum) { + #ifdef WOLFSSL_SEP + case HW_NAME_OID: + /* Only support HW serial number. */ + GetASN_OID(&dataASN[OTHERNAMEASN_IDX_HWN_TYPE], oidIgnoreType); + ret = DecodeSEP(dataASN, cert); + break; + #endif /* WOLFSSL_SEP */ + #ifdef WOLFSSL_FPKI + case FASCN_OID: + case UPN_OID: + ret = DecodeOtherHelper(dataASN, cert, + dataASN[OTHERNAMEASN_IDX_TYPEID].data.oid.sum); + break; + #endif /* WOLFSSL_FPKI */ + default: + WOLFSSL_MSG("\tunsupported OID"); + ret = ASN_PARSE_E; + } + } FREE_ASNGETDATA(dataASN, cert->heap); return ret; } -#endif /* WOLFSSL_SEP */ +#endif /* WOLFSSL_SEP || WOLFSSL_FPKI */ /* Decode a GeneralName. * @@ -14662,7 +14875,7 @@ static int DecodeGeneralName(const byte* input, word32* inOutIdx, byte tag, else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_URI_TYPE)) { WOLFSSL_MSG("\tPutting URI into list but not using"); - #ifndef WOLFSSL_NO_ASN_STRICT + #if !defined(WOLFSSL_NO_ASN_STRICT) && !defined(WOLFSSL_FPKI) /* Verify RFC 5280 Sec 4.2.1.6 rule: "The name MUST NOT be a relative URI" */ { @@ -14708,7 +14921,7 @@ static int DecodeGeneralName(const byte* input, word32* inOutIdx, byte tag, } #endif /* WOLFSSL_QT || OPENSSL_ALL */ #endif /* IGNORE_NAME_CONSTRAINTS */ -#ifdef WOLFSSL_SEP +#if defined(WOLFSSL_SEP) || defined(WOLFSSL_FPKI) /* GeneralName choice: otherName */ else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_OTHER_TYPE)) { /* TODO: test data for code path */ @@ -14759,6 +14972,182 @@ enum { #define altNameASN_Length (sizeof(altNameASN) / sizeof(ASNItem)) #endif /* WOLFSSL_ASN_TEMPLATE */ +#if defined(WOLFSSL_SEP) && !defined(WOLFSSL_ASN_TEMPLATE) +/* return 0 on success */ +static int DecodeSepHwAltName(DecodedCert* cert, const byte* input, + word32* idxIn, int sz) +{ + word32 idx = *idxIn; + int strLen; + int ret; + byte tag; + + /* Certificates issued with this OID in the subject alt name are for + * verifying signatures created on a module. + * RFC 4108 Section 5. */ + if (cert->hwType != NULL) { + WOLFSSL_MSG("\tAlready seen Hardware Module Name"); + return ASN_PARSE_E; + } + + if (GetASNTag(input, &idx, &tag, sz) < 0) { + return ASN_PARSE_E; + } + + if (tag != (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED)) { + WOLFSSL_MSG("\twrong type"); + return ASN_PARSE_E; + } + + if (GetLength(input, &idx, &strLen, sz) < 0) { + WOLFSSL_MSG("\tfail: str len"); + return ASN_PARSE_E; + } + + if (GetSequence(input, &idx, &strLen, sz) < 0) { + WOLFSSL_MSG("\tBad Sequence"); + return ASN_PARSE_E; + } + + ret = GetASNObjectId(input, &idx, &strLen, sz); + if (ret != 0) { + WOLFSSL_MSG("\tbad OID"); + return ret; + } + + cert->hwType = (byte*)XMALLOC(strLen, cert->heap, + DYNAMIC_TYPE_X509_EXT); + if (cert->hwType == NULL) { + WOLFSSL_MSG("\tOut of Memory"); + return MEMORY_E; + } + + XMEMCPY(cert->hwType, &input[idx], strLen); + cert->hwTypeSz = strLen; + idx += strLen; + + ret = GetOctetString(input, &idx, &strLen, sz); + if (ret < 0) { + XFREE(cert->hwType, cert->heap, DYNAMIC_TYPE_X509_EXT); + cert->hwType = NULL; + return ret; + } + + cert->hwSerialNum = (byte*)XMALLOC(strLen + 1, cert->heap, + DYNAMIC_TYPE_X509_EXT); + if (cert->hwSerialNum == NULL) { + WOLFSSL_MSG("\tOut of Memory"); + XFREE(cert->hwType, cert->heap, DYNAMIC_TYPE_X509_EXT); + cert->hwType = NULL; + return MEMORY_E; + } + + XMEMCPY(cert->hwSerialNum, &input[idx], strLen); + cert->hwSerialNum[strLen] = '\0'; + cert->hwSerialNumSz = strLen; + idx += strLen; + + *idxIn = idx; + return 0; +} +#endif /* WOLFSSL_SEP */ + +#if !defined(WOLFSSL_ASN_TEMPLATE) +/* return 0 on success */ +static int DecodeConstructedOtherName(DecodedCert* cert, const byte* input, + word32* idx, int sz, int oid) +{ + int ret = 0; + int strLen = 0; + byte tag; + DNS_entry* dnsEntry = NULL; + + if (GetASNTag(input, idx, &tag, sz) < 0) { + ret = ASN_PARSE_E; + } + + if (ret == 0 && (tag != (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED))) { + ret = ASN_PARSE_E; + } + + if (ret == 0 && (GetLength(input, idx, &strLen, sz) < 0)) { + ret = ASN_PARSE_E; + } + + if (ret == 0) { + dnsEntry = AltNameNew(cert->heap); + if (dnsEntry == NULL) { + WOLFSSL_MSG("\tOut of Memory"); + return MEMORY_E; + } + } + + if (ret == 0) { + switch (oid) { + #ifdef WOLFSSL_FPKI + case FASCN_OID: + ret = GetOctetString(input, idx, &strLen, sz); + if (ret > 0) { + ret = 0; + } + break; + #endif /* WOLFSSL_FPKI */ + case UPN_OID: + if (GetASNTag(input, idx, &tag, sz) < 0) { + ret = ASN_PARSE_E; + } + + if (ret == 0 && + tag != ASN_PRINTABLE_STRING && tag != ASN_UTF8STRING && + tag != ASN_IA5_STRING) { + WOLFSSL_MSG("Was expecting a string for UPN"); + ret = ASN_PARSE_E; + } + + if (ret == 0 && (GetLength(input, idx, &strLen, sz) < 0)) { + WOLFSSL_MSG("Was expecting a string for UPN"); + ret = ASN_PARSE_E; + } + break; + + default: + WOLFSSL_MSG("Unknown constructed other name, skipping"); + *idx += strLen; + XFREE(dnsEntry, cert->heap, DYNAMIC_TYPE_ALTNAME); + dnsEntry = NULL; + } + } + + if (ret == 0 && dnsEntry != NULL) { + dnsEntry->type = ASN_OTHER_TYPE; + dnsEntry->len = strLen; + dnsEntry->name = (char*)XMALLOC(strLen + 1, cert->heap, + DYNAMIC_TYPE_ALTNAME); + #ifdef WOLFSSL_FPKI + dnsEntry->oidSum = oid; + #endif /* WOLFSSL_FPKI */ + if (dnsEntry->name == NULL) { + WOLFSSL_MSG("\tOut of Memory"); + ret = MEMORY_E; + } + else { + XMEMCPY(dnsEntry->name, &input[*idx], strLen); + dnsEntry->name[strLen] = '\0'; + AddAltName(cert, dnsEntry); + } + } + + if (ret == 0) { + *idx += strLen; + } + else { + XFREE(dnsEntry, cert->heap, DYNAMIC_TYPE_ALTNAME); + } + + return ret; +} +#endif + /* Decode subject alternative names extension. * * RFC 5280 4.2.1.6. Subject Alternative Name @@ -14875,7 +15264,6 @@ static int DecodeAltNames(const byte* input, int sz, DecodedCert* cert) dirEntry->len = strLen; XMEMCPY(dirEntry->name, &input[idx], strLen); dirEntry->name[strLen] = '\0'; - dirEntry->next = cert->altDirNames; cert->altDirNames = dirEntry; @@ -14934,7 +15322,7 @@ static int DecodeAltNames(const byte* input, int sz, DecodedCert* cert) return BUFFER_E; } - #ifndef WOLFSSL_NO_ASN_STRICT + #if !defined(WOLFSSL_NO_ASN_STRICT) && !defined(WOLFSSL_FPKI) /* Verify RFC 5280 Sec 4.2.1.6 rule: "The name MUST NOT be a relative URI" */ @@ -15035,14 +15423,12 @@ static int DecodeAltNames(const byte* input, int sz, DecodedCert* cert) } #endif /* WOLFSSL_QT || OPENSSL_ALL */ #endif /* IGNORE_NAME_CONSTRAINTS */ -#ifdef WOLFSSL_SEP else if (b == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_OTHER_TYPE)) { int strLen; word32 lenStartIdx = idx; word32 oid = 0; int ret; - byte tag; if (GetLength(input, &idx, &strLen, sz) < 0) { WOLFSSL_MSG("\tfail: other name length"); @@ -15056,72 +15442,46 @@ static int DecodeAltNames(const byte* input, int sz, DecodedCert* cert) return ASN_PARSE_E; } - if (oid != HW_NAME_OID) { - WOLFSSL_MSG("\tincorrect OID"); - return ASN_PARSE_E; + /* handle parsing other type alt names */ + switch (oid) { + #ifdef WOLFSSL_SEP + case HW_NAME_OID: + ret = DecodeSepHwAltName(cert, input, &idx, sz); + if (ret != 0) + return ret; + break; + #endif /* WOLFSSL_SEP */ + #ifdef WOLFSSL_FPKI + case FASCN_OID: + case UPN_OID: + ret = DecodeConstructedOtherName(cert, input, &idx, sz, + oid); + if (ret != 0) + return ret; + break; + #endif /* WOLFSSL_FPKI */ + + default: + WOLFSSL_MSG("\tUnsupported other name type, skipping"); + if (GetLength(input, &idx, &strLen, sz) < 0) { + /* check to skip constructed other names too */ + if (DecodeConstructedOtherName(cert, input, &idx, sz, + oid) != 0) { + WOLFSSL_MSG("\tfail: unsupported other name length"); + return ASN_PARSE_E; + } + else { + /* idx will have been advanced to end of alt name */ + length -= (idx - lenStartIdx); + } + } + else { + length -= (strLen + idx - lenStartIdx); + idx += strLen; + } } - - /* Certificates issued with this OID in the subject alt name are for - * verifying signatures created on a module. - * RFC 4108 Section 5. */ - if (cert->hwType != NULL) { - WOLFSSL_MSG("\tAlready seen Hardware Module Name"); - return ASN_PARSE_E; - } - - if (GetASNTag(input, &idx, &tag, sz) < 0) { - return ASN_PARSE_E; - } - - if (tag != (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED)) { - WOLFSSL_MSG("\twrong type"); - return ASN_PARSE_E; - } - - if (GetLength(input, &idx, &strLen, sz) < 0) { - WOLFSSL_MSG("\tfail: str len"); - return ASN_PARSE_E; - } - - if (GetSequence(input, &idx, &strLen, sz) < 0) { - WOLFSSL_MSG("\tBad Sequence"); - return ASN_PARSE_E; - } - - ret = GetASNObjectId(input, &idx, &strLen, sz); - if (ret != 0) { - WOLFSSL_MSG("\tbad OID"); - return ret; - } - - cert->hwType = (byte*)XMALLOC(strLen, cert->heap, - DYNAMIC_TYPE_X509_EXT); - if (cert->hwType == NULL) { - WOLFSSL_MSG("\tOut of Memory"); - return MEMORY_E; - } - - XMEMCPY(cert->hwType, &input[idx], strLen); - cert->hwTypeSz = strLen; - idx += strLen; - - ret = GetOctetString(input, &idx, &strLen, sz); - if (ret < 0) - return ret; - - cert->hwSerialNum = (byte*)XMALLOC(strLen + 1, cert->heap, - DYNAMIC_TYPE_X509_EXT); - if (cert->hwSerialNum == NULL) { - WOLFSSL_MSG("\tOut of Memory"); - return MEMORY_E; - } - - XMEMCPY(cert->hwSerialNum, &input[idx], strLen); - cert->hwSerialNum[strLen] = '\0'; - cert->hwSerialNumSz = strLen; - idx += strLen; + (void)ret; } - #endif /* WOLFSSL_SEP */ else { int strLen; word32 lenStartIdx = idx; @@ -16024,6 +16384,17 @@ static int DecodeExtKeyUsage(const byte* input, int sz, DecodedCert* cert) case EKU_OCSP_SIGN_OID: cert->extExtKeyUsage |= EXTKEYUSE_OCSP_SIGN; break; + #ifdef WOLFSSL_WOLFSSH + case EKU_SSH_CLIENT_AUTH_OID: + cert->extExtKeyUsageSsh |= EXTKEYUSE_SSH_CLIENT_AUTH; + break; + case EKU_SSH_MSCL_OID: + cert->extExtKeyUsageSsh |= EXTKEYUSE_SSH_MSCL; + break; + case EKU_SSH_KP_CLIENT_AUTH_OID: + cert->extExtKeyUsageSsh |= EXTKEYUSE_SSH_KP_CLIENT_AUTH; + break; + #endif /* WOLFSSL_WOLFSSH */ default: break; } @@ -16759,6 +17130,216 @@ exit: } #endif /* WOLFSSL_SEP */ +#ifdef WOLFSSL_SUBJ_DIR_ATTR +#ifdef WOLFSSL_ASN_TEMPLATE +/* ASN.1 template for subject dir attribute. + * X.509: RFC 5280, 4.2.1.8 - Subject Directory Attributes. + */ +static const ASNItem subjDirAttrASN[] = { +/* SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 }, +/* SEQ */ { 1, ASN_SEQUENCE, 1, 1, 0 }, +/* OID */ { 2, ASN_OBJECT_ID, 0, 0, 0 }, +/* PLEN */ { 2, ASN_SET, 1, 1, 0 }, +/* BIT_STR */ { 2, ASN_PRINTABLE_STRING, 0, 0, 0 } +}; +enum { + SUBJDIRATTRASN_IDX_SEQ = 0, + SUBJDIRATTRASN_IDX_SEQ2, + SUBJDIRATTRASN_IDX_OID, + SUBJDIRATTRASN_IDX_SET, + SUBJDIRATTRASN_IDX_STRING, +}; + +/* Number of items in ASN.1 template for BasicContraints. */ +#define subjDirAttrASN_Length (sizeof(subjDirAttrASN) / sizeof(ASNItem)) +#endif +/* Decode subject directory attributes extension in a certificate. + * + * X.509: RFC 5280, 4.2.1.8 - Subject Directory Attributes. + * + * @param [in] input Buffer holding data. + * @param [in] sz Size of data in buffer. + * @param [in, out] cert Certificate object. + * @return 0 on success. + * @return ASN_PARSE_E when BER encoded data does not match ASN.1 items or + * is invalid. + */ +static int DecodeSubjDirAttr(const byte* input, int sz, DecodedCert* cert) +{ +#ifndef WOLFSSL_ASN_TEMPLATE + word32 idx = 0; + int length = 0; + int ret = 0; + + WOLFSSL_ENTER("DecodeSubjDirAttr"); + +#ifdef OPENSSL_ALL + cert->extSubjDirAttrSrc = input; + cert->extSubjDirAttrSz = sz; +#endif /* OPENSSL_ALL */ + + /* Unwrap the list of Attributes */ + if (GetSequence(input, &idx, &length, sz) < 0) + return ASN_PARSE_E; + + if (length == 0) { + /* RFC 5280 4.2.1.8. Subject Directory Attributes + If the subjectDirectoryAttributes extension is present, the + sequence MUST contain at least one entry. */ + return ASN_PARSE_E; + } + + /* length is the length of the list contents */ + while (idx < (word32)sz) { + word32 oid; + + if (GetSequence(input, &idx, &length, sz) < 0) + return ASN_PARSE_E; + + if (GetObjectId(input, &idx, &oid, oidSubjDirAttrType, sz) < 0) + return ASN_PARSE_E; + + if (GetSet(input, &idx, &length, sz) < 0) + return ASN_PARSE_E; + + /* There may be more than one countryOfCitizenship, but save the + * first one for now. */ + if (oid == SDA_COC_OID) { + byte tag; + + if (GetHeader(input, &tag, &idx, &length, sz, 1) < 0) + return ASN_PARSE_E; + + if (length != COUNTRY_CODE_LEN) + return ASN_PARSE_E; + + if (tag == ASN_PRINTABLE_STRING) { + XMEMCPY(cert->countryOfCitizenship, + input + idx, COUNTRY_CODE_LEN); + cert->countryOfCitizenship[COUNTRY_CODE_LEN] = 0; + } + } + idx += length; + } + + return ret; +#else + DECL_ASNGETDATA(dataASN, subjDirAttrASN_Length); + int ret = 0; + word32 idx = 0; + + WOLFSSL_ENTER("DecodeSubjDirAttr"); + + CALLOC_ASNGETDATA(dataASN, subjDirAttrASN_Length, ret, cert->heap); + + if (ret == 0) { + ret = GetASN_Items(subjDirAttrASN, dataASN, subjDirAttrASN_Length, 1, + input, &idx, sz); + } + + /* There may be more than one countryOfCitizenship, but save the + * first one for now. */ + if (dataASN[SUBJDIRATTRASN_IDX_OID].data.oid.sum == SDA_COC_OID) { + word32 cuLen; + + cuLen = dataASN[SUBJDIRATTRASN_IDX_STRING].data.ref.length; + if (cuLen != COUNTRY_CODE_LEN) + return ASN_PARSE_E; + + XMEMCPY(cert->countryOfCitizenship, + dataASN[SUBJDIRATTRASN_IDX_STRING].data.ref.data, cuLen); + cert->countryOfCitizenship[COUNTRY_CODE_LEN] = 0; + } + FREE_ASNGETDATA(dataASN, cert->heap); + return ret; +#endif /* WOLFSSL_ASN_TEMPLATE */ +} +#endif /* WOLFSSL_SUBJ_DIR_ATTR */ + +#ifdef WOLFSSL_SUBJ_INFO_ACC +/* Decode subject infomation access extension in a certificate. + * + * X.509: RFC 5280, 4.2.2.2 - Subject Information Access. + * + * @param [in] input Buffer holding data. + * @param [in] sz Size of data in buffer. + * @param [in, out] cert Certificate object. + * @return 0 on success. + * @return ASN_BITSTR_E when the expected BIT_STRING tag is not found. + * @return ASN_PARSE_E when BER encoded data does not match ASN.1 items or + * is invalid. + * @return MEMORY_E on dynamic memory allocation failure. + */ +static int DecodeSubjInfoAcc(const byte* input, int sz, DecodedCert* cert) +{ + word32 idx = 0; + int length = 0; + int ret = 0; + + WOLFSSL_ENTER("DecodeSubjInfoAcc"); + +#ifdef OPENSSL_ALL + cert->extSubjAltNameSrc = input; + cert->extSubjAltNameSz = sz; +#endif /* OPENSSL_ALL */ + + /* Unwrap SubjectInfoAccessSyntax, the list of AccessDescriptions */ + if (GetSequence(input, &idx, &length, sz) < 0) + return ASN_PARSE_E; + + if (length == 0) { + /* RFC 5280 4.2.2.2. Subject Information Access + If the subjectInformationAccess extension is present, the + sequence MUST contain at least one entry. */ + return ASN_PARSE_E; + } + + /* Per fpkx-x509-cert-profile-common... section 5.3. + * [The] subjectInfoAccess extension must contain at least one + * instance of the id-ad-caRepository access method containing a + * publicly accessible HTTP URI which returns as certs-only + * CMS. + */ + + while (idx < (word32)sz) { + word32 oid; + byte b; + + /* Unwrap an AccessDescription */ + if (GetSequence(input, &idx, &length, sz) < 0) + return ASN_PARSE_E; + + /* Get the accessMethod */ + if (GetObjectId(input, &idx, &oid, oidCertAuthInfoType, sz) < 0) + return ASN_PARSE_E; + + /* Only supporting URIs right now. */ + if (GetASNTag(input, &idx, &b, sz) < 0) + return ASN_PARSE_E; + + if (GetLength(input, &idx, &length, sz) < 0) + return ASN_PARSE_E; + + /* Set ocsp entry */ + if (b == GENERALNAME_URI && oid == AIA_OCSP_OID) { + cert->extSubjInfoAccCaRepoSz = length; + cert->extSubjInfoAccCaRepo = input + idx; + break; + } + idx += length; + } + + if (cert->extSubjInfoAccCaRepo == NULL || + cert->extSubjInfoAccCaRepoSz == 0) { + WOLFSSL_MSG("SubjectInfoAccess missing an URL."); + ret = ASN_PARSE_E; + } + + WOLFSSL_LEAVE("DecodeSubjInfoAcc", ret); + return ret; +} +#endif /* WOLFSSL_SUBJ_INFO_ACC */ + /* Macro to check if bit is set, if not sets and return success. Otherwise returns failure */ /* Macro required here because bit-field operation */ @@ -16789,13 +17370,13 @@ exit: * Inhibit anyPolicy - INHIBIT_ANY_OID * Netscape Certificate Type - NETSCAPE_CT_OID (able to be excluded) * OCSP no check - OCSP_NOCHECK_OID (when compiling OCSP) + * Subject Directory Attributes - SUBJ_DIR_ATTR_OID + * Subject Information Access - SUBJ_INFO_ACC_OID * Unsupported extensions from RFC 5280: * 4.2.1.5 - Policy mappings * 4.2.1.7 - Issuer Alternative Name - * 4.2.1.8 - Subject Directory Attributes * 4.2.1.11 - Policy Constraints * 4.2.1.15 - Freshest CRL - * 4.2.2.2 - Subject Information Access * * @param [in] input Buffer containing extension type specific data. * @param [in] length Length of data. @@ -16979,6 +17560,20 @@ static int DecodeExtensionType(const byte* input, int length, word32 oid, if (DecodePolicyConstraints(&input[idx], length, cert) < 0) return ASN_PARSE_E; break; + #ifdef WOLFSSL_SUBJ_DIR_ATTR + case SUBJ_DIR_ATTR_OID: + VERIFY_AND_SET_OID(cert->extSubjDirAttrSet); + if (DecodeSubjDirAttr(&input[idx], length, cert) < 0) + return ASN_PARSE_E; + break; + #endif + #ifdef WOLFSSL_SUBJ_INFO_ACC + case SUBJ_INFO_ACC_OID: + VERIFY_AND_SET_OID(cert->extSubjInfoAccSet); + if (DecodeSubjInfoAcc(&input[idx], length, cert) < 0) + return ASN_PARSE_E; + break; + #endif default: if (isUnknownExt != NULL) *isUnknownExt = 1; @@ -20805,6 +21400,100 @@ int wc_GetPubKeyDerFromCert(struct DecodedCert* cert, return ret; } +#ifdef WOLFSSL_FPKI +/* Search through list for first matching alt name of the same type + * If 'current' is null then the search starts at the head of the list + * otherwise the search starts from the node after 'current' alt name. + * Returns 0 on success + */ +static DNS_entry* FindAltName(struct DecodedCert* cert, int nameType, + DNS_entry* current) +{ + DNS_entry* entry; + + if (current == NULL) { + entry = cert->altNames; + } + else { + entry = current->next; + } + + /* cycle through alt names to check for needed types */ + while (entry != NULL) { + if (entry->type == nameType) { + break; + } + entry = entry->next; + } + + return entry; +} + + +/* returns 0 on success */ +int wc_GetUUIDFromCert(struct DecodedCert* cert, byte* uuid, word32* uuidSz) +{ + int ret = ALT_NAME_E; + DNS_entry* id = NULL; + + do { + id = FindAltName(cert, ASN_URI_TYPE, id); + if (id != NULL) { + /* check if URI string matches expected format for UUID */ + if (id->len != DEFAULT_UUID_SZ) { + continue; /* size not right not a UUID URI */ + } + + if (XMEMCMP(id->name, "urn:uuid:", 9) != 0) { + continue; /* beginning text not right for a UUID URI */ + } + + if (uuid == NULL) { + *uuidSz = id->len; + return LENGTH_ONLY_E; + } + + if ((int)*uuidSz < id->len) { + return BUFFER_E; + } + + XMEMCPY(uuid, id->name, id->len); + ret = 0; /* success */ + break; + } + } while (id != NULL); + + return ret; +} + + +/* reutrns 0 on success */ +int wc_GetFASCNFromCert(struct DecodedCert* cert, byte* fascn, word32* fascnSz) +{ + int ret = ALT_NAME_E; + DNS_entry* id = NULL; + + do { + id = FindAltName(cert, ASN_OTHER_TYPE, id); + if (id != NULL && id->oidSum == FASCN_OID) { + if (fascn == NULL) { + *fascnSz = id->len; + return LENGTH_ONLY_E; + } + + if ((int)*fascnSz < id->len) { + return BUFFER_E; + } + + XMEMCPY(fascn, id->name, id->len); + ret = 0; /* success */ + } + } while (id != NULL); + + return ret; +} +#endif /* WOLFSSL_FPKI */ + #if !defined(NO_RSA) && (defined(WOLFSSL_CERT_GEN) || \ defined(WOLFSSL_KCAPI_RSA) || \ ((defined(WOLFSSL_KEY_GEN) || defined(OPENSSL_EXTRA)) && !defined(HAVE_USER_RSA))) diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index fd442ad02..a82d60f0f 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -975,6 +975,8 @@ enum Misc_ASN { PEM_LINE_SZ = 64, /* Length of Base64 encoded line, not including new line */ PEM_LINE_LEN = PEM_LINE_SZ + 12, /* PEM line max + fudge */ + + COUNTRY_CODE_LEN = 2, /* RFC 3739 */ }; #ifndef WC_MAX_NAME_ENTRIES @@ -1010,6 +1012,9 @@ enum Oid_Types { oidTlsExtType = 18, oidCrlExtType = 19, oidCsrAttrType = 20, +#ifdef WOLFSSL_SUBJ_DIR_ATTR + oidSubjDirAttrType = 21, +#endif oidIgnoreType }; @@ -1129,7 +1134,7 @@ enum Extensions_Sum { EXT_KEY_USAGE_OID = 151, /* 2.5.29.37 */ NAME_CONS_OID = 144, /* 2.5.29.30 */ PRIV_KEY_USAGE_PERIOD_OID = 130, /* 2.5.29.16 */ - SUBJECT_INFO_ACCESS = 79, /* 1.3.6.1.5.5.7.1.11 */ + SUBJ_INFO_ACC_OID = 79, /* 1.3.6.1.5.5.7.1.11 */ POLICY_MAP_OID = 147, /* 2.5.29.33 */ POLICY_CONST_OID = 150, /* 2.5.29.36 */ ISSUE_ALT_NAMES_OID = 132, /* 2.5.29.18 */ @@ -1137,13 +1142,22 @@ enum Extensions_Sum { NETSCAPE_CT_OID = 753, /* 2.16.840.1.113730.1.1 */ OCSP_NOCHECK_OID = 121, /* 1.3.6.1.5.5.7.48.1.5 id-pkix-ocsp-nocheck */ + SUBJ_DIR_ATTR_OID = 123, /* 2.5.29.9 */ - AKEY_PACKAGE_OID = 1048 /* 2.16.840.1.101.2.1.2.78.5 + AKEY_PACKAGE_OID = 1048, /* 2.16.840.1.101.2.1.2.78.5 RFC 5958 - Asymmetric Key Packages */ + FASCN_OID = 419, /* 2.16.840.1.101.3.6.6 Federal PKI Policy FASC-N */ + UPN_OID = 265 /* 1.3.6.1.4.1.311.20.2.3 UPN */ }; enum CertificatePolicy_Sum { - CP_ANY_OID = 146 /* id-ce 32 0 */ + CP_ANY_OID = 146, /* id-ce 32 0 */ +#ifdef WOLFSSL_FPKI + CP_FPKI_COMMON_AUTH_OID = 426, /* 2.16.840.1.101.3.2.1.3.13 */ + CP_FPKI_PIV_AUTH_OID = 453, /* 2.16.840.1.101.3.2.1.3.40 */ + CP_FPKI_PIV_AUTH_HW_OID = 454, /* 2.16.840.1.101.3.2.1.3.41 */ + CP_FPKI_PIVI_AUTH_OID = 458 /* 2.16.840.1.101.3.2.1.3.45 */ +#endif /* WOLFSSL_FPKI */ }; enum SepHardwareName_Sum { @@ -1151,10 +1165,15 @@ enum SepHardwareName_Sum { }; enum AuthInfo_Sum { - AIA_OCSP_OID = 116, /* 1.3.6.1.5.5.7.48.1 */ - AIA_CA_ISSUER_OID = 117 /* 1.3.6.1.5.5.7.48.2 */ + AIA_OCSP_OID = 116, /* 1.3.6.1.5.5.7.48.1, id-ad-ocsp */ + AIA_CA_ISSUER_OID = 117, /* 1.3.6.1.5.5.7.48.2, id-ad-caIssuers */ + #ifdef WOLFSSL_SUBJ_INFO_ACC + AIA_CA_REPO_OID = 120 /* 1.3.6.1.5.5.7.48.5, id-ad-caRepository */ + #endif /* WOLFSSL_SUBJ_INFO_ACC */ }; +#define ID_PKIX(num) (67+(num)) /* 1.3.6.1.5.5.7.num, id-pkix num */ +#define ID_KP(num) (ID_PKIX(3)+(num)) /* 1.3.6.1.5.5.7.3.num, id-kp num */ enum ExtKeyUsage_Sum { /* From RFC 5280 */ EKU_ANY_OID = 151, /* 2.5.29.37.0, anyExtendedKeyUsage */ EKU_SERVER_AUTH_OID = 71, /* 1.3.6.1.5.5.7.3.1, id-kp-serverAuth */ @@ -1162,9 +1181,27 @@ enum ExtKeyUsage_Sum { /* From RFC 5280 */ EKU_CODESIGNING_OID = 73, /* 1.3.6.1.5.5.7.3.3, id-kp-codeSigning */ EKU_EMAILPROTECT_OID = 74, /* 1.3.6.1.5.5.7.3.4, id-kp-emailProtection */ EKU_TIMESTAMP_OID = 78, /* 1.3.6.1.5.5.7.3.8, id-kp-timeStamping */ - EKU_OCSP_SIGN_OID = 79 /* 1.3.6.1.5.5.7.3.9, id-kp-OCSPSigning */ + EKU_OCSP_SIGN_OID = 79, /* 1.3.6.1.5.5.7.3.9, id-kp-OCSPSigning */ + + /* From RFC 6187: X.509v3 Certificates for Secure Shell Authenticaiton */ + EKU_SSH_CLIENT_AUTH_OID = ID_KP(21), /* id-kp-secureShellClient */ + EKU_SSH_MSCL_OID = 264, + /* 1.3.6.1.4.1.311.20.2.2, MS Smart Card Logon */ + EKU_SSH_KP_CLIENT_AUTH_OID = 64 + /* 1.3.6.1.5.2.3.4, id-pkinit-KPClientAuth*/ }; +#ifdef WOLFSSL_SUBJ_DIR_ATTR +#define ID_PDA(num) (ID_PKIX(9)+(num)) /* 1.3.6.1.5.5.7.9.num, id-pda num */ +enum SubjDirAttr_Sum { /* From RFC 3739, section 3.3.2 */ + SDA_DOB_OID = ID_PDA(1), /* id-pda-dateOfBirth */ + SDA_POB_OID = ID_PDA(2), /* id-pda-placeOfBirth */ + SDA_GENDER_OID = ID_PDA(3), /* id-pda-gender */ + SDA_COC_OID = ID_PDA(4), /* id-pda-countryOfCitizenship */ + SDA_COR_OID = ID_PDA(5) /* id-pda-countryOfResidence */ +}; +#endif /* WOLFSSL_SUBJ_DIR_ATTR */ + #ifdef HAVE_LIBZ enum CompressAlg_Sum { ZLIBc = 679 /* 1.2.840.113549.1.9.16.3.8, id-alg-zlibCompress */ @@ -1223,6 +1260,11 @@ enum CsrAttrType { #define EXTKEYUSE_CLIENT_AUTH 0x04 #define EXTKEYUSE_SERVER_AUTH 0x02 #define EXTKEYUSE_ANY 0x01 +#ifdef WOLFSSL_WOLFSSH + #define EXTKEYUSE_SSH_CLIENT_AUTH 0x01 + #define EXTKEYUSE_SSH_MSCL 0x02 + #define EXTKEYUSE_SSH_KP_CLIENT_AUTH 0x04 +#endif /* WOLFSSL_WOLFSSH */ #define WC_NS_SSL_CLIENT 0x80 #define WC_NS_SSL_SERVER 0x40 @@ -1252,8 +1294,15 @@ struct DNS_entry { #if defined(OPENSSL_ALL) || defined(WOLFSSL_IP_ALT_NAME) char* ipString; /* human readable form of IP address */ #endif +#ifdef WOLFSSL_FPKI + int oidSum; /* provide oid sum for verification */ +#endif }; +#ifdef WOLFSSL_FPKI + /* RFC4122 i.e urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6 */ + #define DEFAULT_UUID_SZ 45 +#endif typedef struct Base_entry Base_entry; @@ -1535,6 +1584,9 @@ struct DecodedCert { byte policyConstSkip; /* Policy Constraints skip certs value */ word16 extKeyUsage; /* Key usage bitfield */ byte extExtKeyUsage; /* Extended Key usage bitfield */ +#ifdef WOLFSSL_WOLFSSH + byte extExtKeyUsageSsh; /* Extended Key Usage bitfield for SSH */ +#endif /* WOLFSSL_WOLFSSH */ #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) const byte* extExtKeyUsageSrc; @@ -1553,6 +1605,21 @@ struct DecodedCert { const byte* extSubjAltNameSrc; word32 extSubjAltNameSz; #endif +#ifdef WOLFSSL_SUBJ_DIR_ATTR + char countryOfCitizenship[COUNTRY_CODE_LEN+1]; /* ISO 3166 Country Code */ + #ifdef OPENSSL_ALL + const byte* extSubjDirAttrSrc; + word32 extSubjDirAttrSz; + #endif +#endif /* WOLFSSL_SUBJ_DIR_ATTR */ +#ifdef WOLFSSL_SUBJ_INFO_ACC + const byte* extSubjInfoAccCaRepo; + word32 extSubjInfoAccCaRepoSz; + #ifdef OPENSSL_ALL + const byte* extSubjInfoAccSrc; + word32 extSubjInfoAccSz; + #endif +#endif /* WOLFSSL_SUBJ_INFO_ACC */ #if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) word32 pkCurveOID; /* Public Key's curve OID */ @@ -1732,6 +1799,12 @@ struct DecodedCert { byte extSubjKeyIdCrit : 1; byte extKeyUsageCrit : 1; byte extExtKeyUsageCrit : 1; +#ifdef WOLFSSL_SUBJ_DIR_ATTR + byte extSubjDirAttrSet : 1; +#endif +#ifdef WOLFSSL_SUBJ_INFO_ACC + byte extSubjInfoAccSet : 1; +#endif #if defined(WOLFSSL_SEP) || defined(WOLFSSL_QT) byte extCertPolicyCrit : 1; #endif diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index bafbd24d6..f9351a765 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -830,6 +830,12 @@ WOLFSSL_API int wc_ParseCert( WOLFSSL_API int wc_GetPubKeyDerFromCert(struct DecodedCert* cert, byte* derKey, word32* derKeySz); +#ifdef WOLFSSL_FPKI +WOLFSSL_API int wc_GetUUIDFromCert(struct DecodedCert* cert, + byte* uuid, word32* uuidSz); +WOLFSSL_API int wc_GetFASCNFromCert(struct DecodedCert* cert, + byte* fascn, word32* fascnSz); +#endif /* WOLFSSL_FPKI */ #ifdef __cplusplus } /* extern "C" */ #endif