diff --git a/certs/acert/acert.pem b/certs/acert/acert.pem new file mode 100644 index 0000000000..4bde876d3e --- /dev/null +++ b/certs/acert/acert.pem @@ -0,0 +1,23 @@ +-----BEGIN ATTRIBUTE CERTIFICATE----- +MIID4zCCAssCAQEwOaA3MB+kHTAbMRkwFwYDVQQDDBBUUE0gTWFudWZhY3R1cmVy +AhRADHoGLYO7i9GfV2Yz2rrlRFDPSqA6MDikNjA0MQswCQYDVQQGEwJVUzEUMBIG +A1UECgwLZXhhbXBsZS5jb20xDzANBgNVBAsMBlBDVGVzdDANBgkqhkiG9w0BAQsF +AAIBATAiGA8yMDE4MDEwMTA1MDAwMFoYDzIwMjgwMTAxMDUwMDAwWjCB7TALBgVn +gQUCEzECMAAwHAYFZ4EFAhExEzARMAkCAQECAQMCARYEBAAAAAEwEgYFZ4EFAhkx +CTAHBgVngQUIAjCBlQYHZ4EFBQEHAjGBiTCBhqBkMC8wDgYGZ4EFEgMBBAQAAgAB +DBJYWVogQ29tcHV0aW5nIEluYy4MATGABkFCQzEyMzAxMA4GBmeBBRIDAQQEAAcA +AgwNTm90IFNwZWNpZmllZAwDSEQxgAgxMjM0QUJDRIMB/6IeMBwMCHVuYW1lIC1y +DBA2LjUuMC0xNS1nZW5lcmljMBQGBWeBBQIXMQswCQIBAQIBAQIBETCCAScwbwYD +VR0jBGgwZoAUl46DRCrPD3GZndkBbbNDngf6ZHChOKQ2MDQxCzAJBgNVBAYTAlVT +MRQwEgYDVQQKDAtleGFtcGxlLmNvbTEPMA0GA1UECwwGUENUZXN0ghRmuv6Ey2Ja +dCAOFysMNOn9CiH45zBBBgNVHSAEOjA4MDYGAioDMDAwLgYIKwYBBQUHAgIwIgwg +VENHIFRydXN0ZWQgUGxhdGZvcm0gRW5kb3JzZW1lbnQwcQYDVR0RBGowaKRmMGQx +EzARBgZngQUFAQQMB01vZGVsIEExHjAcBgZngQUFAQEMElhZWiBDb21wdXRpbmcg +SW5jLjEZMBcGBmeBBQUBBQwNTm90IFNwZWNpZmllZDESMBAGBmeBBQUBBgwGQUJD +MTIzMA0GCSqGSIb3DQEBCwUAA4IBAQB2SdELM7Dqaq2mvT+IV3pCBN7qPzRL+sO4 +MZG6jpTbbblr124KM84g936zLVZxOJeAa+Ie7r0ET7GYI+zKtpLmIZrlqhZl4YkP +3g65JsIVc5PvOogxv67IxVigHu/NFKHIbFPz85drTatEVCfA8ac8BwJXXwuESLNr +cH+K/vdLWDgMhsijhco82RI8x11wBvzMXLPnM5OnkiG/0zaEW7mk1gH2tBS6oCc+ +0v8y9jQ5NqyPo0mNhLJhUMonmvaGdZ3iDEFyF+iNuDc3pP5PA1YDKk/BYGXt1NUE +89mkuGoF8bwkU9uqLKQ3jpCKx/SZZ08IK5MPQyzsnwjyhrsrP3Qm +-----END ATTRIBUTE CERTIFICATE----- diff --git a/certs/acert/acert_ietf.pem b/certs/acert/acert_ietf.pem new file mode 100644 index 0000000000..1c25c86779 --- /dev/null +++ b/certs/acert/acert_ietf.pem @@ -0,0 +1,15 @@ +-----BEGIN ATTRIBUTE CERTIFICATE----- +MIICPTCCASUCAQEwN6AWMBGkDzANMQswCQYDVQQDDAJDQQIBAqEdpBswGTEXMBUG +A1UEAwwOc2VydmVyLmV4YW1wbGWgLTArpCkwJzElMCMGA1UEAwwcQXR0cmlidXRl +IENlcnRpZmljYXRlIElzc3VlcjANBgkqhkiG9w0BAQsFAAIUA7WQWQKiqrVAIUS4 +LE/ZgBtfV8IwIhgPMjAyMTA2MTUxMjM1MDBaGA8yMDMxMDYxMzEyMzUwMFowQTAj +BggrBgEFBQcKBDEXMBWgCYYHVGVzdHZhbDAIDAZncm91cDEwGgYDVQRIMRMwEaEP +gw1hZG1pbmlzdHJhdG9yMCwwHwYDVR0jBBgwFoAUYm7JaGdsZLtTgt0tqoCK2MrI +i10wCQYDVR04BAIFADANBgkqhkiG9w0BAQsFAAOCAQEAlIOJ2Dj3TEUj6BIv6vUs +GqFWms05i+d10XSzWrunlUTQPoJcUjYkifOWp/7RpZ2XnRl+6hH+nIbmwSmXWwBn +ERw2bQMmw//nWuN4Qv9t7ltuovWC0pJX6VMT1IRTuTV4SxuZpFL37vkmnFlPBlb+ +mn3ESSxLTjThWFIq1tip4IaxE/i5Uh32GlJglatFHM1PCGoJtyLtYb6KHDlvknw6 +coDyjIcj0FZwtQw41jLwxI8jWNmrpt978wdpprB/URrRs+m02HmeQoiHFi/qvdv8 +d+5vHf3Pi/ulhz/+dvr0p1vEQSoFnYxLXuty2p5m3PJPZCFmT3gURgmgR3BN9d7A +Bw== +-----END ATTRIBUTE CERTIFICATE----- diff --git a/certs/acert/acert_ietf_pubkey.pem b/certs/acert/acert_ietf_pubkey.pem new file mode 100644 index 0000000000..c7434f9344 --- /dev/null +++ b/certs/acert/acert_ietf_pubkey.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqvpigJZE2asRTFe63b3f +xvh0swQuX+L4hW08E7mlm0NSQvBVs8yebELNnZLL738fvocvQMwAjf+8+Lyjb1fr +FYMYvJpb6LmGA2Ysyt6Ny700dpiUValtd4mwtjSCH0/k4rCiaiCYWaN79Le9ZGwD +pZ341kVX74JkNdaXs1EJ1tkUUoq6aIu5CWYncxjA4IufduHV1Eh/dpNq1tuLHjgY +Y3NwYDJcotmN9mmIO+MAuZ1TzifhIy14tNGIspYpSZbn8j2RQpQOclhMVWeM5t0i +TWgOO+jhJngptIJMXEaQQzKPiazv6pBhk8oamAZ0Nipr+DI8iDxvzHtyFDRVToOg +1QIDAQAB +-----END PUBLIC KEY----- diff --git a/certs/acert/acert_pubkey.pem b/certs/acert/acert_pubkey.pem new file mode 100644 index 0000000000..a382a1be06 --- /dev/null +++ b/certs/acert/acert_pubkey.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArjl1VnpENuEfQCVm2E4q +h28D62c0pX5IgN5F2RoS7siU2Oc9hsSz6Hj+9o0SRhUTEAxxrML2d7TM2SVoIJ/x +CFrchA1fIZQm7FWJa7MDFpxkRc7cNUGrZ5oyVCHtK6IbKiU4y8B/vova6+dyy6bi +j97ea0UDL8ztKNyDUH9ZntyFrHTltA/ZlEjmxGHQJQd4RBO6RdfM70R7l+YTGa2N +PflyiRY2SKNXXx8cVUURJvkOXVfLCuRUzG+NnSS62WRuWOOD0ZjiJCnwkTJZQNw0 +qI+hLhWN+//05JeKOw6rNVVUHR/R0GgjPL6FIQ/+yF2Z8nCd8lVIIY+hQsM/1l/h +2QIDAQAB +-----END PUBLIC KEY----- diff --git a/certs/acert/include.am b/certs/acert/include.am new file mode 100644 index 0000000000..e93fe85d4d --- /dev/null +++ b/certs/acert/include.am @@ -0,0 +1,13 @@ +# vim:ft=automake +# All paths should be given relative to the root +# + +EXTRA_DIST += \ + certs/acert/acert.pem \ + certs/acert/acert_ietf.pem \ + certs/acert/acert_pubkey.pem \ + certs/acert/acert_ietf_pubkey.pem \ + certs/acert/rsa_pss/acert.pem \ + certs/acert/rsa_pss/acert_ietf.pem \ + certs/acert/rsa_pss/acert_pubkey.pem \ + certs/acert/rsa_pss/acert_ietf_pubkey.pem diff --git a/certs/acert/rsa_pss/acert.pem b/certs/acert/rsa_pss/acert.pem new file mode 100644 index 0000000000..74519df31d --- /dev/null +++ b/certs/acert/rsa_pss/acert.pem @@ -0,0 +1,25 @@ +-----BEGIN ATTRIBUTE CERTIFICATE----- +MIIESzCCAv8CAQEwOaA3MB+kHTAbMRkwFwYDVQQDDBBUUE0gTWFudWZhY3R1cmVy +AhRADHoGLYO7i9GfV2Yz2rrlRFDPSqA6MDikNjA0MQswCQYDVQQGEwJVUzEUMBIG +A1UECgwLZXhhbXBsZS5jb20xDzANBgNVBAsMBlBDVGVzdDBBBgkqhkiG9w0BAQow +NKAPMA0GCWCGSAFlAwQCAQUAoRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAQUA +ogMCASACAQEwIhgPMjAxODAxMDEwNTAwMDBaGA8yMDI4MDEwMTA1MDAwMFowge0w +CwYFZ4EFAhMxAjAAMBwGBWeBBQIRMRMwETAJAgEBAgEDAgEWBAQAAAABMBIGBWeB +BQIZMQkwBwYFZ4EFCAIwgZUGB2eBBQUBBwIxgYkwgYagZDAvMA4GBmeBBRIDAQQE +AAIAAQwSWFlaIENvbXB1dGluZyBJbmMuDAExgAZBQkMxMjMwMTAOBgZngQUSAwEE +BAAHAAIMDU5vdCBTcGVjaWZpZWQMA0hEMYAIMTIzNEFCQ0SDAf+iHjAcDAh1bmFt +ZSAtcgwQNi41LjAtMTUtZ2VuZXJpYzAUBgVngQUCFzELMAkCAQECAQECAREwggEn +MG8GA1UdIwRoMGaAFJeOg0Qqzw9xmZ3ZAW2zQ54H+mRwoTikNjA0MQswCQYDVQQG +EwJVUzEUMBIGA1UECgwLZXhhbXBsZS5jb20xDzANBgNVBAsMBlBDVGVzdIIUZrr+ +hMtiWnQgDhcrDDTp/Qoh+OcwQQYDVR0gBDowODA2BgIqAzAwMC4GCCsGAQUFBwIC +MCIMIFRDRyBUcnVzdGVkIFBsYXRmb3JtIEVuZG9yc2VtZW50MHEGA1UdEQRqMGik +ZjBkMRMwEQYGZ4EFBQEEDAdNb2RlbCBBMR4wHAYGZ4EFBQEBDBJYWVogQ29tcHV0 +aW5nIEluYy4xGTAXBgZngQUFAQUMDU5vdCBTcGVjaWZpZWQxEjAQBgZngQUFAQYM +BkFCQzEyMzBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQCAQUAoRwwGgYJKoZI +hvcNAQEIMA0GCWCGSAFlAwQCAQUAogMCASADggEBAH4FGu9CJJ/NraWxXoB+EuHu +Ec95MPJDHnsDLea45z5TXkdxCd8Tb5EBuWYFCI6nkpWtkiF5UaLncQD/1ag0ECjZ +duhmoaM01t8TERP1x2xOotpiS0nDGiAqn3twBS3NZlxgEDRMvW92tM49Vvlk7JwD +Kxv9+qXidCXt62dcDNJoe1Uj9HXxuOO2NaO9OQHlPkY5GctKbcDBwaDUlEz40J9k +PoXDNurLmI/nNgMDgicKdzdmhMT/BSXSt7Z228p7QcgROgJ5xTEVIMm+lGcBg1Sc +RnWTVNjrIG+/nzYZENr+F40nrIKbkIIZTLCqwAN6fFFt/jNc44SdoJMNsKe1bTM= +-----END ATTRIBUTE CERTIFICATE----- diff --git a/certs/acert/rsa_pss/acert_ietf.pem b/certs/acert/rsa_pss/acert_ietf.pem new file mode 100644 index 0000000000..37f75625fb --- /dev/null +++ b/certs/acert/rsa_pss/acert_ietf.pem @@ -0,0 +1,17 @@ +-----BEGIN ATTRIBUTE CERTIFICATE----- +MIICpTCCAVkCAQEwN6AWMBGkDzANMQswCQYDVQQDDAJDQQIBAqEdpBswGTEXMBUG +A1UEAwwOc2VydmVyLmV4YW1wbGWgLTArpCkwJzElMCMGA1UEAwwcQXR0cmlidXRl +IENlcnRpZmljYXRlIElzc3VlcjBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQC +AQUAoRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAQUAogMCASACFAO1kFkCoqq1 +QCFEuCxP2YAbX1fCMCIYDzIwMjEwNjE1MTIzNTAwWhgPMjAzMTA2MTMxMjM1MDBa +MEEwIwYIKwYBBQUHCgQxFzAVoAmGB1Rlc3R2YWwwCAwGZ3JvdXAxMBoGA1UESDET +MBGhD4MNYWRtaW5pc3RyYXRvcjAsMB8GA1UdIwQYMBaAFGJuyWhnbGS7U4LdLaqA +itjKyItdMAkGA1UdOAQCBQAwQQYJKoZIhvcNAQEKMDSgDzANBglghkgBZQMEAgEF +AKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgEFAKIDAgEgA4IBAQCX18Lyj2CR +AJL9JAwxYgWbk7fWif2mG5IiQ264Rd0W6ugqw1hYseKHnRI5LpxsRVF5kEaFs2ta +FhwxxOtAo8YvbxMC4emuatbqwOlWQYwk9wPLbZb1nd1FItPtO98FK7/vF0263eJu +A+UFxmDvLlao3SzP19mtCOcUjGsVxcJ2PN05wDUzITu2vGXuJAdjHcYX+s1UMLwk +WMwHsz7EK2Al/FavI1MfZp0lVFi++CMOAdLIRbTjlACATDq6Q6kPc+bTqvMYoca2 +bGLw1jSig6T3DvGa3O/BwRMOhyqCtJNQYY7MYxcZhPR4Y0RLmyFnFiSzwypL6oMk +QMaW0z/K5YO2 +-----END ATTRIBUTE CERTIFICATE----- diff --git a/certs/acert/rsa_pss/acert_ietf_pubkey.pem b/certs/acert/rsa_pss/acert_ietf_pubkey.pem new file mode 100644 index 0000000000..f2d208d393 --- /dev/null +++ b/certs/acert/rsa_pss/acert_ietf_pubkey.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIDALBgkqhkiG9w0BAQoDggEPADCCAQoCggEBALg9nrRhxCl5zxFdE7Le9GXL +9M8Rzx5xU3meu6yp9lFIc3+FxNoc5E8nk7HXUK82iuEChcSlqt0j0/y03YqM+O45 +N6A9OkEkjdyL8BaeQEgNxZY16/nvhhnH0Bzg4n7DMvy3sUPQvsAu9tpbfSd+WNDT +vtO9Fe84HIBkYhRuaIv7ca1UYn7R2VQk1RXK0lfY4orCOrexmlfPciJaTJcR5Lyi +pjUj7X5lruRHVibrMY+Z+8DtvPaDZ7HFiuXzpGPQ0W907Wt7zEJfmTMUyQoOMDMM +4iSlq0ib3rdZt9y2obCggRTFAtMAFIJ29FOT9FYDagMYFSqhnrR3ohiTNzfpYNMC +AwEAAQ== +-----END PUBLIC KEY----- diff --git a/certs/acert/rsa_pss/acert_pubkey.pem b/certs/acert/rsa_pss/acert_pubkey.pem new file mode 100644 index 0000000000..a0f2d828a0 --- /dev/null +++ b/certs/acert/rsa_pss/acert_pubkey.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIDALBgkqhkiG9w0BAQoDggEPADCCAQoCggEBAL0P9mcosJbMQavKMo6FvjK/ +vC5PZAFYxsbQnDiG3kb3gCsshI8HQzHNIuw4wN3waJrqnFmsmsUqMENtsC0J2Fty +DOI5791Ma7JUKT31RW6f5eU2Gjx1+evNWtWs2WzupsZdPS3DlgEQJsTSw3Fs1q5w +JVLVHhtOjCwdj2QO9Xr17Nt0ZOfKoJdqth3LAVujMnOw9gbyTbCrCB+z1Mkq+dK4 +K0v6IPZqY76LVhR42y/lyG+MZ8jswg4I4qAE+iIwPi/9Tz9UdNwMfSr3gdD13pa3 +VqnGZG83prqPLEHwsSNpWGdDx7pQxgBkAPztO+7LPrMd1ck8Uugsq36pusLjdQ0C +AwEAAQ== +-----END PUBLIC KEY----- diff --git a/certs/include.am b/certs/include.am index d4417fe8e3..f3f1f6a362 100644 --- a/certs/include.am +++ b/certs/include.am @@ -148,4 +148,5 @@ include certs/rsapss/include.am include certs/dilithium/include.am include certs/sphincs/include.am include certs/rpk/include.am +include certs/acert/include.am diff --git a/configure.ac b/configure.ac index 81edc5c6c3..355510e8b1 100644 --- a/configure.ac +++ b/configure.ac @@ -3843,6 +3843,12 @@ then ENABLED_KEYGEN=yes fi +# ATTRIBUTE CERTIFICATES +AC_ARG_ENABLE([acert], + [AS_HELP_STRING([--enable-acert],[Enable attribute certificate support (default: disabled)])], + [ ENABLED_ACERT=$enableval ], + [ ENABLED_ACERT=no ] + ) # CERT GENERATION AC_ARG_ENABLE([certgen], @@ -9240,6 +9246,9 @@ AS_IF([test "x$ENABLED_ALTNAMES" = "xyes"], AS_IF([test "x$ENABLED_KEYGEN" = "xyes"], [AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_KEY_GEN"]) +AS_IF([test "x$ENABLED_ACERT" = "xyes"], + [AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_ACERT"]) + AS_IF([test "x$ENABLED_CERTREQ" = "xyes"], [AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_CERT_REQ"]) @@ -10230,6 +10239,7 @@ echo " * BLAKE2S: $ENABLED_BLAKE2S" echo " * SipHash: $ENABLED_SIPHASH" echo " * CMAC: $ENABLED_CMAC" echo " * keygen: $ENABLED_KEYGEN" +echo " * acert: $ENABLED_ACERT" echo " * certgen: $ENABLED_CERTGEN" echo " * certreq: $ENABLED_CERTREQ" echo " * certext: $ENABLED_CERTEXT" diff --git a/src/internal.c b/src/internal.c index 1b0a47d08e..747dc621ae 100644 --- a/src/internal.c +++ b/src/internal.c @@ -12640,6 +12640,45 @@ static void AddSessionCertToChain(WOLFSSL_X509_CHAIN* chain, } #endif +#if defined(KEEP_PEER_CERT) || defined(SESSION_CERTS) || \ + defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) || \ + defined(WOLFSSL_ACERT) + static int CopyAltNames(DNS_entry** to, DNS_entry* from, int type, void* heap) +{ + /* Copy from to the beginning of to */ + DNS_entry** prev_next = to; + DNS_entry* next; + + if (to == NULL) { + return BAD_FUNC_ARG; + } + + next = *to; + + for (; from != NULL; from = from->next) { + DNS_entry* dnsEntry; + + if (type != -1 && from->type != type) + continue; + + dnsEntry = AltNameDup(from, heap); + if (dnsEntry == NULL) { + WOLFSSL_MSG("\tOut of Memory"); + return MEMORY_E; + } + + dnsEntry->next = next; + *prev_next = dnsEntry; + prev_next = &dnsEntry->next; + } + + return 0; +} +#endif /* KEEP_PEER_CERT || SESSION_CERTS || + * OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL || + * WOLFSSL_ACERT */ + + #if defined(KEEP_PEER_CERT) || defined(SESSION_CERTS) || \ defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) void CopyDecodedName(WOLFSSL_X509_NAME* name, DecodedCert* dCert, int nameType) @@ -12674,38 +12713,6 @@ void CopyDecodedName(WOLFSSL_X509_NAME* name, DecodedCert* dCert, int nameType) } } -static int CopyAltNames(DNS_entry** to, DNS_entry* from, int type, void* heap) -{ - /* Copy from to the beginning of to */ - DNS_entry** prev_next = to; - DNS_entry* next; - - if (to == NULL) { - return BAD_FUNC_ARG; - } - - next = *to; - - for (; from != NULL; from = from->next) { - DNS_entry* dnsEntry; - - if (type != -1 && from->type != type) - continue; - - dnsEntry = AltNameDup(from, heap); - if (dnsEntry == NULL) { - WOLFSSL_MSG("\tOut of Memory"); - return MEMORY_E; - } - - dnsEntry->next = next; - *prev_next = dnsEntry; - prev_next = &dnsEntry->next; - } - - return 0; -} - #ifdef WOLFSSL_CERT_REQ static int CopyREQAttributes(WOLFSSL_X509* x509, DecodedCert* dCert) { @@ -13212,6 +13219,122 @@ int CopyDecodedToX509(WOLFSSL_X509* x509, DecodedCert* dCert) #endif /* KEEP_PEER_CERT || SESSION_CERTS */ +#if defined(WOLFSSL_ACERT) +/* Copy a DecodedAcert structure to an X509_ACERT. + * + * @param [out] x509 the dst X509 acert structure + * @param [in] dAcert the src decoded acert structure + * + * @return 0 on success + * @return < 0 on error + * */ +int CopyDecodedAcertToX509(WOLFSSL_X509_ACERT* x509, DecodedAcert* dAcert) +{ + int ret = 0; + + if (x509 == NULL || dAcert == NULL) { + return BAD_FUNC_ARG; + } + + /* Copy version and serial number. */ + x509->version = dAcert->version + 1; + + XMEMCPY(x509->serial, dAcert->serial, EXTERNAL_SERIAL_SIZE); + x509->serialSz = dAcert->serialSz; + + if (dAcert->holderSerialSz > 0) { + /* This ACERT Holder field had a serial number. Copy it. */ + XMEMCPY(x509->holderSerial, dAcert->holderSerial, + dAcert->holderSerialSz); + x509->holderSerialSz = dAcert->holderSerialSz; + } + + /* Copy before and after dates. */ + { + int minSz = 0; + + if (dAcert->beforeDateLen > 0) { + minSz = (int)min(dAcert->beforeDate[1], MAX_DATE_SZ); + x509->notBefore.type = dAcert->beforeDate[0]; + x509->notBefore.length = minSz; + XMEMCPY(x509->notBefore.data, &dAcert->beforeDate[2], minSz); + } + else { + x509->notBefore.length = 0; + } + + if (dAcert->afterDateLen > 0) { + minSz = (int)min(dAcert->afterDate[1], MAX_DATE_SZ); + x509->notAfter.type = dAcert->afterDate[0]; + x509->notAfter.length = minSz; + XMEMCPY(x509->notAfter.data, &dAcert->afterDate[2], minSz); + } + else { + x509->notAfter.length = 0; + } + } + + /* Copy the signature. */ + if (dAcert->signature != NULL && dAcert->sigLength != 0 && + dAcert->sigLength <= MAX_ENCODED_SIG_SZ) { + x509->sig.buffer = (byte*)XMALLOC( + dAcert->sigLength, x509->heap, DYNAMIC_TYPE_SIGNATURE); + if (x509->sig.buffer == NULL) { + ret = MEMORY_E; + } + else { + XMEMCPY(x509->sig.buffer, dAcert->signature, dAcert->sigLength); + x509->sig.length = dAcert->sigLength; + x509->sigOID = (int)dAcert->signatureOID; + } + } + + /* if der contains original source buffer then store for potential + * retrieval */ + if (dAcert->source != NULL && dAcert->maxIdx > 0) { + if (AllocDer(&x509->derCert, dAcert->maxIdx, CERT_TYPE, x509->heap) + == 0) { + XMEMCPY(x509->derCert->buffer, dAcert->source, dAcert->maxIdx); + } + else { + ret = MEMORY_E; + } + } + + /* Copy holder and att cert issuer names if present. */ + if (CopyAltNames(&x509->holderIssuerName, dAcert->holderIssuerName, + ASN_DIR_TYPE, x509->heap) != 0) { + return MEMORY_E; + } + + if (CopyAltNames(&x509->holderEntityName, dAcert->holderEntityName, + ASN_DIR_TYPE, x509->heap) != 0) { + return MEMORY_E; + } + + if (CopyAltNames(&x509->AttCertIssuerName, dAcert->AttCertIssuerName, + ASN_DIR_TYPE, x509->heap) != 0) { + return MEMORY_E; + } + + if (dAcert->rawAttr && dAcert->rawAttrLen > 0) { + /* Allocate space for the raw Attributes field, then copy it in. */ + x509->rawAttr = (byte*)XMALLOC(dAcert->rawAttrLen, x509->heap, + DYNAMIC_TYPE_X509_EXT); + if (x509->rawAttr != NULL) { + XMEMCPY(x509->rawAttr, dAcert->rawAttr, dAcert->rawAttrLen); + x509->rawAttrLen = dAcert->rawAttrLen; + } + else { + ret = MEMORY_E; + } + } + + return ret; +} +#endif /* WOLFSSL_ACERT */ + + #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ (defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) && !defined(WOLFSSL_NO_TLS12)) static int ProcessCSR(WOLFSSL* ssl, byte* input, word32* inOutIdx, diff --git a/src/x509.c b/src/x509.c index 24bdb9f62a..759e1fc6f1 100644 --- a/src/x509.c +++ b/src/x509.c @@ -5828,6 +5828,248 @@ int wolfSSL_X509_cmp(const WOLFSSL_X509 *a, const WOLFSSL_X509 *b) #define MAX_WIDTH 80 #endif +#if defined(WOLFSSL_ACERT) +#define ACERT_NUM_DIR_TAGS 4 + +/* Convenience struct and function for printing the Holder sub fields + * of an X509 Attribute struct. */ +struct acert_dir_print_t { + const char * pfx; + const byte tag[3]; +}; + +static struct acert_dir_print_t acert_dir_print[ACERT_NUM_DIR_TAGS] = +{ + { "C=", {0x55, 0x04, ASN_COUNTRY_NAME} }, + { "O=", {0x55, 0x04, ASN_ORG_NAME} }, + { "OU=", {0x55, 0x04, ASN_ORGUNIT_NAME} }, + { "CN=", {0x55, 0x04, ASN_COMMON_NAME} }, +}; + +/* Print an entry of ASN_DIR_TYPE into dst of length max_len. + * + * Returns total_len of str on success. + * Returns < 0 on failure. + * */ +static int X509PrintDirType(char * dst, int max_len, const DNS_entry * entry) +{ + word32 k = 0; + word32 i = 0; + const char * src = entry->name; + word32 src_len = (word32)XSTRLEN(src); + int total_len = 0; + int bytes_left = max_len; + int fld_len = 0; + int match_found = 0; + + XMEMSET(dst, 0, max_len); + + /* loop over printable DIR tags. */ + for (k = 0; k < ACERT_NUM_DIR_TAGS; ++k) { + const char * pfx = acert_dir_print[k].pfx; + const byte * tag = acert_dir_print[k].tag; + byte asn_tag; + + /* walk through entry looking for matches. */ + for (i = 0; i < src_len - 5; ++i) { + if (XMEMCMP(tag, &src[i], 3) == 0) { + if (bytes_left < 5) { + /* Not enough space left for name oid + tag + len. */ + break; + } + + if (match_found) { + /* append a {',', ' '} before doing anything else. */ + *dst++ = ','; + *dst++ = ' '; + total_len += 2; + bytes_left -= 2; + } + + i += 3; + + /* Get the ASN Tag. */ + if (GetASNTag((const byte *)src, &i, &asn_tag, src_len) < 0) { + WOLFSSL_MSG("error: GetASNTag failed"); + break; + } + + /* Check it is printable. */ + if ((asn_tag != ASN_PRINTABLE_STRING) && + (asn_tag != ASN_IA5_STRING) && + (asn_tag != ASN_UTF8STRING)) { + /* Don't know what this is but we can't print it. */ + WOLFSSL_MSG("error: asn tag not printable string"); + break; + } + + /* Now get the length of the printable string. */ + if (GetLength((const byte *)src, &i, &fld_len, src_len) < 0) { + break; + } + + /* Make sure we have space to fit it. */ + if ((int) XSTRLEN(pfx) > bytes_left) { + /* Not enough space left. */ + break; + } + + /* Copy it in, decrement available space. */ + XSTRNCPY(dst, pfx, bytes_left); + dst += XSTRLEN(pfx); + total_len += XSTRLEN(pfx); + bytes_left -= XSTRLEN(pfx); + + if (fld_len > bytes_left) { + /* Not enough space left. */ + break; + } + + XMEMCPY(dst, &src[i], fld_len); + i += fld_len; + dst += fld_len; + total_len += fld_len; + bytes_left -= fld_len; + + match_found = 1; + } + } + } + + return total_len; +} + +static int X509_ACERT_print_name_entry(WOLFSSL_BIO* bio, + const DNS_entry* entry, int indent) +{ + int ret = WOLFSSL_SUCCESS; + int nameCount = 0; + char scratch[MAX_WIDTH]; + int len; + + if (bio == NULL || entry == NULL) { + return WOLFSSL_FAILURE; + } + + len = XSNPRINTF(scratch, MAX_WIDTH, "%*s", indent, ""); + if (len >= MAX_WIDTH) { + return WOLFSSL_FAILURE; + } + + if (wolfSSL_BIO_write(bio, scratch, (int)XSTRLEN(scratch)) <= 0) { + return WOLFSSL_FAILURE; + } + + while (entry != NULL) { + ++nameCount; + if (nameCount > 1) { + if (wolfSSL_BIO_write(bio, ", ", 2) <= 0) { + ret = WOLFSSL_FAILURE; + break; + } + } + + if (entry->type == ASN_DNS_TYPE) { + len = XSNPRINTF(scratch, MAX_WIDTH, "DNS:%s", entry->name); + if (len >= MAX_WIDTH) { + ret = WOLFSSL_FAILURE; + break; + } + } + #if defined(OPENSSL_ALL) || defined(WOLFSSL_IP_ALT_NAME) + else if (entry->type == ASN_IP_TYPE) { + len = XSNPRINTF(scratch, MAX_WIDTH, "IP Address:%s", + entry->ipString); + if (len >= MAX_WIDTH) { + ret = WOLFSSL_FAILURE; + break; + } + } + #endif /* OPENSSL_ALL || WOLFSSL_IP_ALT_NAME */ + else if (entry->type == ASN_RFC822_TYPE) { + len = XSNPRINTF(scratch, MAX_WIDTH, "email:%s", + entry->name); + if (len >= MAX_WIDTH) { + ret = WOLFSSL_FAILURE; + break; + } + } + else if (entry->type == ASN_DIR_TYPE) { + len = X509PrintDirType(scratch, MAX_WIDTH, entry); + if (len >= MAX_WIDTH) { + ret = WOLFSSL_FAILURE; + break; + } + } + else if (entry->type == ASN_URI_TYPE) { + len = XSNPRINTF(scratch, MAX_WIDTH, "URI:%s", + entry->name); + if (len >= MAX_WIDTH) { + ret = WOLFSSL_FAILURE; + break; + } + } + #if defined(OPENSSL_ALL) + else if (entry->type == ASN_RID_TYPE) { + len = XSNPRINTF(scratch, MAX_WIDTH, "Registered ID:%s", + entry->ridString); + if (len >= MAX_WIDTH) { + ret = WOLFSSL_FAILURE; + break; + } + } + #endif + else if (entry->type == ASN_OTHER_TYPE) { + len = XSNPRINTF(scratch, MAX_WIDTH, + "othername "); + if (len >= MAX_WIDTH) { + ret = WOLFSSL_FAILURE; + break; + } + } + else { + WOLFSSL_MSG("Bad alt name type."); + ret = WOLFSSL_FAILURE; + break; + } + + if (wolfSSL_BIO_write(bio, scratch, (int)XSTRLEN(scratch)) + <= 0) { + ret = WOLFSSL_FAILURE; + break; + } + + entry = entry->next; + } + + if (ret == WOLFSSL_SUCCESS && wolfSSL_BIO_write(bio, "\n", 1) <= 0) { + ret = WOLFSSL_FAILURE; + } + + return ret; +} + +/* Sets buf pointer and len to raw Attribute buffer and buffer len + * in X509 struct. + * + * Returns WOLFSSL_SUCCESS on success. + * Returns BAD_FUNC_ARG if input pointers are null. + * */ +WOLFSSL_API int wolfSSL_X509_ACERT_get_attr_buf(const WOLFSSL_X509_ACERT* x509, + const byte ** rawAttr, + word32 * rawAttrLen) +{ + if (x509 == NULL || rawAttr == NULL || rawAttrLen == NULL) { + return BAD_FUNC_ARG; + } + + *rawAttr = x509->rawAttr; + *rawAttrLen = x509->rawAttrLen; + + return WOLFSSL_SUCCESS; +} +#endif /* if WOLFSSL_ACERT*/ + static int X509PrintSubjAltName(WOLFSSL_BIO* bio, WOLFSSL_X509* x509, int indent) { @@ -6156,6 +6398,70 @@ static int X509PrintSerial(WOLFSSL_BIO* bio, WOLFSSL_X509* x509, int indent) return WOLFSSL_SUCCESS; } +#ifndef NO_ASN_TIME +static int X509PrintValidity(WOLFSSL_BIO* bio, WOLFSSL_ASN1_TIME * notBefore, + WOLFSSL_ASN1_TIME * notAfter, int indent) +{ + char tmp[80]; + (void) indent; + + if (wolfSSL_BIO_write(bio, " Validity\n", + (int)XSTRLEN(" Validity\n")) <= 0) { + return WOLFSSL_FAILURE; + } + + if (wolfSSL_BIO_write(bio, " Not Before: ", + (int)XSTRLEN(" Not Before: ")) <= 0) { + return WOLFSSL_FAILURE; + } + if (notBefore->length > 0) { + if (GetTimeString(notBefore->data, ASN_UTC_TIME, + tmp, sizeof(tmp)) != WOLFSSL_SUCCESS) { + if (GetTimeString(notBefore->data, ASN_GENERALIZED_TIME, + tmp, sizeof(tmp)) != WOLFSSL_SUCCESS) { + WOLFSSL_MSG("Error getting not before date"); + return WOLFSSL_FAILURE; + } + } + } + else { + XSTRNCPY(tmp, "Not Set", sizeof(tmp)-1); + } + tmp[sizeof(tmp) - 1] = '\0'; /* make sure null terminated */ + if (wolfSSL_BIO_write(bio, tmp, (int)XSTRLEN(tmp)) <= 0) { + return WOLFSSL_FAILURE; + } + + if (wolfSSL_BIO_write(bio, "\n Not After : ", + (int)XSTRLEN("\n Not After : ")) <= 0) { + return WOLFSSL_FAILURE; + } + if (notAfter->length > 0) { + if (GetTimeString(notAfter->data, ASN_UTC_TIME, + tmp, sizeof(tmp)) != WOLFSSL_SUCCESS) { + if (GetTimeString(notAfter->data, ASN_GENERALIZED_TIME, + tmp, sizeof(tmp)) != WOLFSSL_SUCCESS) { + WOLFSSL_MSG("Error getting not after date"); + return WOLFSSL_FAILURE; + } + } + } + else { + XSTRNCPY(tmp, "Not Set", sizeof(tmp)-1); + } + tmp[sizeof(tmp) - 1] = '\0'; /* make sure null terminated */ + if (wolfSSL_BIO_write(bio, tmp, (int)XSTRLEN(tmp)) <= 0) { + return WOLFSSL_FAILURE; + } + + if (wolfSSL_BIO_write(bio, "\n\0", (int)XSTRLEN("\n\0")) <= 0) { + return WOLFSSL_FAILURE; + } + + return WOLFSSL_SUCCESS; +} +#endif /* ifndef NO_ASN_TIME */ + /* iterate through certificate extensions printing them out in human readable * form * return WOLFSSL_SUCCESS on success @@ -6757,199 +7063,582 @@ int wolfSSL_X509_REQ_print(WOLFSSL_BIO* bio, WOLFSSL_X509* x509) { char subjType[] = "Subject: "; - if (bio == NULL || x509 == NULL) { - return WOLFSSL_FAILURE; - } + if (bio == NULL || x509 == NULL) { + return WOLFSSL_FAILURE; + } + + if (wolfSSL_BIO_write(bio, "Certificate Request:\n", + (int)XSTRLEN("Certificate Request:\n")) <= 0) { + return WOLFSSL_FAILURE; + } + + if (wolfSSL_BIO_write(bio, " Data:\n", + (int)XSTRLEN(" Data:\n")) <= 0) { + return WOLFSSL_FAILURE; + } + + /* print version of cert */ + if (X509PrintVersion(bio, wolfSSL_X509_version(x509), 8) + != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + + if (X509PrintSerial(bio, x509, 8) != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + + /* print subject */ + if (X509PrintName(bio, wolfSSL_X509_get_subject_name(x509), subjType, 8) + != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + + /* get and print public key */ + if (X509PrintPubKey(bio, x509, 8) != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + + /* print out extensions */ + if (X509PrintExtensions(bio, x509, 4) != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + + /* print out req attributes */ + if (X509PrintReqAttributes(bio, x509, 4) != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + + /* print out signature */ + if (X509PrintSignature(bio, x509, 0, 4) != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + + /* done with print out */ + if (wolfSSL_BIO_write(bio, "\n\0", (int)XSTRLEN("\n\0")) <= 0) { + return WOLFSSL_FAILURE; + } + + return WOLFSSL_SUCCESS; +} +#endif /* WOLFSSL_CERT_REQ */ + + +/* Writes the human readable form of x509 to bio. + * + * bio WOLFSSL_BIO to write to. + * x509 Certificate to write. + * + * returns WOLFSSL_SUCCESS on success and WOLFSSL_FAILURE on failure + */ +int wolfSSL_X509_print_ex(WOLFSSL_BIO* bio, WOLFSSL_X509* x509, + unsigned long nmflags, unsigned long cflag) +{ + char issuType[] = "Issuer:"; + char subjType[] = "Subject:"; + + WOLFSSL_ENTER("wolfSSL_X509_print_ex"); + + /* flags currently not supported */ + (void)nmflags; + (void)cflag; + + if (bio == NULL || x509 == NULL) { + return WOLFSSL_FAILURE; + } + + if (wolfSSL_BIO_write(bio, "Certificate:\n", + (int)XSTRLEN("Certificate:\n")) <= 0) { + return WOLFSSL_FAILURE; + } + + if (wolfSSL_BIO_write(bio, " Data:\n", + (int)XSTRLEN(" Data:\n")) <= 0) { + return WOLFSSL_FAILURE; + } + + /* print version of cert */ + if (X509PrintVersion(bio, wolfSSL_X509_version(x509), 8) + != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + + /* print serial number out */ + if (X509PrintSerial(bio, x509, 8) != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + + /* print out signature algo*/ + if (X509PrintSignature(bio, x509, 1, 8) != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + + /* print issuer */ + if (X509PrintName(bio, wolfSSL_X509_get_issuer_name(x509), issuType, 8) + != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + + #ifndef NO_ASN_TIME + /* print validity */ + if (X509PrintValidity(bio, &x509->notBefore, &x509->notAfter, 8) + != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + #endif /* NO_ASN_TIME */ + + /* print subject */ + if (X509PrintName(bio, wolfSSL_X509_get_subject_name(x509), subjType, 8) + != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + + /* get and print public key */ + if (X509PrintPubKey(bio, x509, 8) != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + + /* print out extensions */ + if (X509PrintExtensions(bio, x509, 8) != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + + /* print out signature */ + if (X509PrintSignature(bio, x509, 0, 4) != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + + /* done with print out */ + if (wolfSSL_BIO_write(bio, "\n\0", (int)XSTRLEN("\n\0")) <= 0) { + return WOLFSSL_FAILURE; + } + + return WOLFSSL_SUCCESS; +} +int wolfSSL_X509_print(WOLFSSL_BIO* bio, WOLFSSL_X509* x509) +{ + return wolfSSL_X509_print_ex(bio, x509, 0, 0); +} + +#if defined(WOLFSSL_ACERT) +WOLFSSL_X509_ACERT * wolfSSL_X509_ACERT_load_certificate_buffer( + const unsigned char* buf, int sz, int format) +{ + int ret = 0; + WOLFSSL_X509_ACERT * x509 = NULL; + DerBuffer * der = NULL; + #ifdef WOLFSSL_SMALL_STACK + DecodedAcert * acert = NULL; + #else + DecodedAcert acert[1]; + #endif + + WOLFSSL_ENTER("wolfSSL_X509_ACERT_load_certificate_buffer"); + + if (format == WOLFSSL_FILETYPE_PEM) { + #ifdef WOLFSSL_PEM_TO_DER + ret = PemToDer(buf, sz, ACERT_TYPE, &der, NULL, NULL, NULL); + + if (ret != 0 || der == NULL || der->buffer == NULL) { + WOLFSSL_ERROR(ret); + + if (der != NULL) { + FreeDer(&der); + } + + return NULL; + } + #else + WOLFSSL_ERROR(NOT_COMPILED_IN); + return NULL; + #endif + } + else { + ret = AllocDer(&der, (word32)sz, ACERT_TYPE, NULL); + + if (ret != 0 || der == NULL || der->buffer == NULL) { + WOLFSSL_ERROR(ret); + return NULL; + } + + XMEMCPY(der->buffer, buf, sz); + } + + #ifdef WOLFSSL_SMALL_STACK + acert = (DecodedAcert*)XMALLOC(sizeof(DecodedAcert), NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (acert == NULL) { + WOLFSSL_ERROR(MEMORY_ERROR); + FreeDer(&der); + return NULL; + } + #endif + + InitDecodedAcert(acert, der->buffer, der->length, NULL); + + ret = ParseX509Acert(acert, VERIFY_SKIP_DATE); + + if (ret == 0) { + x509 = (WOLFSSL_X509_ACERT*)XMALLOC(sizeof(WOLFSSL_X509_ACERT), NULL, + DYNAMIC_TYPE_X509_ACERT); + if (x509 != NULL) { + wolfSSL_X509_ACERT_init(x509, NULL); + ret = CopyDecodedAcertToX509(x509, acert); + + if (ret != 0) { + wolfSSL_X509_ACERT_free(x509); + x509 = NULL; + } + } + else { + ret = MEMORY_ERROR; + } + } + + FreeDecodedAcert(acert); + + #ifdef WOLFSSL_SMALL_STACK + XFREE(acert, NULL, DYNAMIC_TYPE_DCERT); + #endif + + FreeDer(&der); + + if (ret != 0) { + WOLFSSL_ERROR(ret); + } + + return x509; +} + +void wolfSSL_X509_ACERT_init(WOLFSSL_X509_ACERT * x509, void* heap) +{ + if (x509 == NULL) { + WOLFSSL_MSG("error: InitX509Acert: null parameter"); + return; + } + + XMEMSET(x509, 0, sizeof(*x509)); + + x509->heap = heap; +} + +void wolfSSL_X509_ACERT_free(WOLFSSL_X509_ACERT* x509) +{ + if (x509 == NULL) { + WOLFSSL_MSG("error: wolfSSL_X509_ACERT_free: null parameter"); + return; + } + + /* Free holder and att cert issuer structures. */ + if (x509->holderIssuerName) { + FreeAltNames(x509->holderIssuerName, x509->heap); + x509->holderIssuerName = NULL; + } + + if (x509->AttCertIssuerName) { + FreeAltNames(x509->AttCertIssuerName, x509->heap); + x509->AttCertIssuerName = NULL; + } + + if (x509->rawAttr != NULL) { + XFREE(x509->rawAttr, x509->heap, DYNAMIC_TYPE_X509_EXT); + x509->rawAttr = NULL; + x509->rawAttrLen = 0; + } + + /* Free derCert source and signature buffer. */ + FreeDer(&x509->derCert); + + if (x509->sig.buffer != NULL) { + XFREE(x509->sig.buffer, x509->heap, DYNAMIC_TYPE_SIGNATURE); + x509->sig.buffer = NULL; + } + + /* Finally memset and free x509 acert structure. */ + XMEMSET(x509, 0, sizeof(*x509)); + XFREE(x509, x509->heap, NULL); + + return; +} + +long wolfSSL_X509_ACERT_get_version(const WOLFSSL_X509_ACERT* x509) +{ + int version = 0; + + if (x509 == NULL) { + return 0L; + } + + version = x509->version; + + return version != 0 ? (long)version - 1L : 0L; +} + +int wolfSSL_X509_ACERT_version(WOLFSSL_X509_ACERT* x509) +{ + if (x509 == NULL) { + return 0; + } + + return x509->version; +} + +/* Retrieve sig NID from an ACERT. + * + * returns NID on success + * returns 0 on failure + */ +int wolfSSL_X509_ACERT_get_signature_nid(const WOLFSSL_X509_ACERT *x509) +{ + if (x509 == NULL) { + return 0; + } + + return oid2nid((word32)x509->sigOID, oidSigType); +} + +/* Retrieve the signature from an ACERT. + * + * @param [in] x509 the x509 attribute certificate + * @param [in, out] buf the signature buffer pointer + * @param [in, out] bufSz the signature buffer size pointer + * + * buf may be null, but bufSz is required. On success, sets + * bufSz pointer to signature length, and copies signature + * to buf if provided. + * + * Returns WWOLFSSL_FATAL_ERROR if bufSz is null or too small. + * Returns WOLFSSL_SUCCESS on success. + */ +int wolfSSL_X509_ACERT_get_signature(WOLFSSL_X509_ACERT* x509, + unsigned char* buf, int* bufSz) +{ + WOLFSSL_ENTER("wolfSSL_X509_ACERT_get_signature"); + + if (x509 == NULL || bufSz == NULL) { + return WOLFSSL_FATAL_ERROR; + } + + /* If buf array is provided, it must be long enough. */ + if (buf != NULL && *bufSz < (int)x509->sig.length) { + return WOLFSSL_FATAL_ERROR; + } + + if (buf != NULL) { + /* Copy in buffer if provided. */ + XMEMCPY(buf, x509->sig.buffer, x509->sig.length); + } + + *bufSz = (int)x509->sig.length; + + return WOLFSSL_SUCCESS; +} + +static int X509AcertPrintSignature(WOLFSSL_BIO* bio, WOLFSSL_X509_ACERT* x509, + int algOnly, int indent) +{ + int sigSz = 0; + if (wolfSSL_X509_ACERT_get_signature(x509, NULL, &sigSz) <= 0) { + return WOLFSSL_FAILURE; + } + + if (sigSz > 0) { + unsigned char* sig; + int sigNid; + + sigNid = wolfSSL_X509_ACERT_get_signature_nid(x509); + if (sigNid <= 0) { + return WOLFSSL_FAILURE; + } + + sig = (unsigned char*)XMALLOC(sigSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (sig == NULL) { + return WOLFSSL_FAILURE; + } - if (wolfSSL_BIO_write(bio, "Certificate Request:\n", - (int)XSTRLEN("Certificate Request:\n")) <= 0) { + if (wolfSSL_X509_ACERT_get_signature(x509, sig, &sigSz) <= 0) { + XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER); return WOLFSSL_FAILURE; - } + } - if (wolfSSL_BIO_write(bio, " Data:\n", - (int)XSTRLEN(" Data:\n")) <= 0) { + if (X509PrintSignature_ex(bio, sig, sigSz, sigNid, algOnly, indent) + != WOLFSSL_SUCCESS) { + XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER); return WOLFSSL_FAILURE; - } + } - /* print version of cert */ - if (X509PrintVersion(bio, wolfSSL_X509_version(x509), 8) - != WOLFSSL_SUCCESS) { - return WOLFSSL_FAILURE; - } + if (sig != NULL) { + XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } - if (X509PrintSerial(bio, x509, 8) != WOLFSSL_SUCCESS) { - return WOLFSSL_FAILURE; } - /* print subject */ - if (X509PrintName(bio, wolfSSL_X509_get_subject_name(x509), subjType, 8) - != WOLFSSL_SUCCESS) { - return WOLFSSL_FAILURE; - } + return WOLFSSL_SUCCESS; +} - /* get and print public key */ - if (X509PrintPubKey(bio, x509, 8) != WOLFSSL_SUCCESS) { - return WOLFSSL_FAILURE; - } +/* Retrieve the serial number from an ACERT. + * + * @param [in] x509 the x509 attribute certificate + * @param [in, out] buf the serial number buffer pointer + * @param [in, out] bufSz the serial number buffer size pointer + * + * buf may be null, but bufSz is required. On success, sets + * bufSz pointer to signature length, and copies signature + * to buf if provided. + * + * Returns WWOLFSSL_FATAL_ERROR if bufSz is null or too small. + * Returns WOLFSSL_SUCCESS on success. + */ +int wolfSSL_X509_ACERT_get_serial_number(WOLFSSL_X509_ACERT* x509, + byte* buf, int* bufSz) +{ + WOLFSSL_ENTER("wolfSSL_X509_ACERT_get_serial_number"); - /* print out extensions */ - if (X509PrintExtensions(bio, x509, 4) != WOLFSSL_SUCCESS) { - return WOLFSSL_FAILURE; + if (x509 == NULL || bufSz == NULL) { + WOLFSSL_MSG("error: null argument passed in"); + return BAD_FUNC_ARG; } - /* print out req attributes */ - if (X509PrintReqAttributes(bio, x509, 4) != WOLFSSL_SUCCESS) { - return WOLFSSL_FAILURE; - } + if (buf != NULL) { + if (*bufSz < x509->serialSz) { + WOLFSSL_MSG("error: serial buffer too small"); + return BUFFER_E; + } - /* print out signature */ - if (X509PrintSignature(bio, x509, 0, 4) != WOLFSSL_SUCCESS) { - return WOLFSSL_FAILURE; + XMEMCPY(buf, x509->serial, x509->serialSz); } - /* done with print out */ - if (wolfSSL_BIO_write(bio, "\n\0", (int)XSTRLEN("\n\0")) <= 0) { - return WOLFSSL_FAILURE; - } + *bufSz = x509->serialSz; return WOLFSSL_SUCCESS; } -#endif /* WOLFSSL_CERT_REQ */ - -/* Writes the human readable form of x509 to bio. - * - * bio WOLFSSL_BIO to write to. - * x509 Certificate to write. - * - * returns WOLFSSL_SUCCESS on success and WOLFSSL_FAILURE on failure - */ -int wolfSSL_X509_print_ex(WOLFSSL_BIO* bio, WOLFSSL_X509* x509, - unsigned long nmflags, unsigned long cflag) +static int X509AcertPrintSerial(WOLFSSL_BIO* bio, WOLFSSL_X509_ACERT* x509, + int indent) { - char issuType[] = "Issuer:"; - char subjType[] = "Subject:"; + unsigned char serial[32]; + int sz = sizeof(serial); - WOLFSSL_ENTER("wolfSSL_X509_print_ex"); + XMEMSET(serial, 0, sz); + if (wolfSSL_X509_ACERT_get_serial_number(x509, serial, &sz) + == WOLFSSL_SUCCESS) { + X509PrintSerial_ex(bio, serial, sz, 1, indent); + } + return WOLFSSL_SUCCESS; +} - /* flags currently not supported */ - (void)nmflags; - (void)cflag; +int wolfSSL_X509_ACERT_print(WOLFSSL_BIO* bio, WOLFSSL_X509_ACERT* x509) +{ + const char * hdr = "Attribute Certificate:\n"; + const char * data_hdr = " Data:\n"; + const char * holder_hdr = " Holder:\n"; + const char * holder_issuer_hdr = " Issuer:"; + const char * holder_name_hdr = " Name:"; + const char * attcert_issuer_hdr = " Issuer:"; if (bio == NULL || x509 == NULL) { return WOLFSSL_FAILURE; } - if (wolfSSL_BIO_write(bio, "Certificate:\n", - (int)XSTRLEN("Certificate:\n")) <= 0) { - return WOLFSSL_FAILURE; + /* print acert header */ + if (wolfSSL_BIO_write(bio, hdr, (int)XSTRLEN(hdr)) <= 0) { + return WOLFSSL_FAILURE; } - if (wolfSSL_BIO_write(bio, " Data:\n", - (int)XSTRLEN(" Data:\n")) <= 0) { - return WOLFSSL_FAILURE; + /* print data header */ + if (wolfSSL_BIO_write(bio, data_hdr, (int)XSTRLEN(data_hdr)) <= 0) { + return WOLFSSL_FAILURE; } /* print version of cert */ - if (X509PrintVersion(bio, wolfSSL_X509_version(x509), 8) + if (X509PrintVersion(bio, wolfSSL_X509_ACERT_version(x509), 8) != WOLFSSL_SUCCESS) { return WOLFSSL_FAILURE; } /* print serial number out */ - if (X509PrintSerial(bio, x509, 8) != WOLFSSL_SUCCESS) { - return WOLFSSL_FAILURE; - } - - /* print out signature algo*/ - if (X509PrintSignature(bio, x509, 1, 8) != WOLFSSL_SUCCESS) { + if (X509AcertPrintSerial(bio, x509, 8) != WOLFSSL_SUCCESS) { return WOLFSSL_FAILURE; } - /* print issuer */ - if (X509PrintName(bio, wolfSSL_X509_get_issuer_name(x509), issuType, 8) - != WOLFSSL_SUCCESS) { + /* print holder field */ + if (wolfSSL_BIO_write(bio, holder_hdr, (int)XSTRLEN(holder_hdr)) <= 0) { return WOLFSSL_FAILURE; } -#ifndef NO_ASN_TIME - /* print validity */ - { - char tmp[80]; - - if (wolfSSL_BIO_write(bio, " Validity\n", - (int)XSTRLEN(" Validity\n")) <= 0) { + if (x509->holderEntityName != NULL) { + /* print issuer header */ + if (wolfSSL_BIO_write(bio, holder_name_hdr, + (int)XSTRLEN(holder_name_hdr)) <= 0) { return WOLFSSL_FAILURE; } - if (wolfSSL_BIO_write(bio, " Not Before: ", - (int)XSTRLEN(" Not Before: ")) <= 0) { + if (X509_ACERT_print_name_entry(bio, x509->holderEntityName, 1) + != WOLFSSL_SUCCESS) { return WOLFSSL_FAILURE; } - if (x509->notBefore.length > 0) { - if (GetTimeString(x509->notBefore.data, ASN_UTC_TIME, - tmp, sizeof(tmp)) != WOLFSSL_SUCCESS) { - if (GetTimeString(x509->notBefore.data, ASN_GENERALIZED_TIME, - tmp, sizeof(tmp)) != WOLFSSL_SUCCESS) { - WOLFSSL_MSG("Error getting not before date"); - return WOLFSSL_FAILURE; - } - } - } - else { - XSTRNCPY(tmp, "Not Set", sizeof(tmp)-1); - } - tmp[sizeof(tmp) - 1] = '\0'; /* make sure null terminated */ - if (wolfSSL_BIO_write(bio, tmp, (int)XSTRLEN(tmp)) <= 0) { + } + + if (x509->holderIssuerName != NULL) { + /* print issuer header */ + if (wolfSSL_BIO_write(bio, holder_issuer_hdr, + (int)XSTRLEN(holder_issuer_hdr)) <= 0) { return WOLFSSL_FAILURE; } - if (wolfSSL_BIO_write(bio, "\n Not After : ", - (int)XSTRLEN("\n Not After : ")) <= 0) { + if (X509_ACERT_print_name_entry(bio, x509->holderIssuerName, 1) + != WOLFSSL_SUCCESS) { return WOLFSSL_FAILURE; } - if (x509->notAfter.length > 0) { - if (GetTimeString(x509->notAfter.data, ASN_UTC_TIME, - tmp, sizeof(tmp)) != WOLFSSL_SUCCESS) { - if (GetTimeString(x509->notAfter.data, ASN_GENERALIZED_TIME, - tmp, sizeof(tmp)) != WOLFSSL_SUCCESS) { - WOLFSSL_MSG("Error getting not after date"); - return WOLFSSL_FAILURE; - } - } - } - else { - XSTRNCPY(tmp, "Not Set", sizeof(tmp)-1); - } - tmp[sizeof(tmp) - 1] = '\0'; /* make sure null terminated */ - if (wolfSSL_BIO_write(bio, tmp, (int)XSTRLEN(tmp)) <= 0) { + } + + if (x509->holderSerialSz > 0) { + X509PrintSerial_ex(bio, x509->holderSerial, x509->holderSerialSz, + 1, 12); + } + + /* print issuer header */ + if (wolfSSL_BIO_write(bio, attcert_issuer_hdr, + (int)XSTRLEN(attcert_issuer_hdr)) <= 0) { + return WOLFSSL_FAILURE; + } + + if (x509->AttCertIssuerName != NULL) { + if (X509_ACERT_print_name_entry(bio, x509->AttCertIssuerName, 1) + != WOLFSSL_SUCCESS) { return WOLFSSL_FAILURE; } - - if (wolfSSL_BIO_write(bio, "\n\0", (int)XSTRLEN("\n\0")) <= 0) { + } + else { + const char * msg = " Issuer type not supported.\n"; + if (wolfSSL_BIO_write(bio, msg, (int)XSTRLEN(msg)) <= 0) { return WOLFSSL_FAILURE; } } - #endif - /* print subject */ - if (X509PrintName(bio, wolfSSL_X509_get_subject_name(x509), subjType, 8) - != WOLFSSL_SUCCESS) { + #ifndef NO_ASN_TIME + /* print validity */ + if (X509PrintValidity(bio, &x509->notBefore, &x509->notAfter, 8) + != WOLFSSL_SUCCESS) { return WOLFSSL_FAILURE; } + #endif /* NO_ASN_TIME */ - /* get and print public key */ - if (X509PrintPubKey(bio, x509, 8) != WOLFSSL_SUCCESS) { - return WOLFSSL_FAILURE; - } + /* print raw attributes */ + if (x509->rawAttr && x509->rawAttrLen > 0) { + char attr_hdr[128]; /* buffer for XSNPRINTF */ - /* print out extensions */ - if (X509PrintExtensions(bio, x509, 8) != WOLFSSL_SUCCESS) { - return WOLFSSL_FAILURE; + if (XSNPRINTF(attr_hdr, 128, "%*s%s: %d bytes\n", 8, "", + "Attributes", x509->rawAttrLen) >= 128) { + return WOLFSSL_FAILURE; + } + + if (wolfSSL_BIO_write(bio, attr_hdr, (int)XSTRLEN(attr_hdr)) <= 0) { + return WOLFSSL_FAILURE; + } } - /* print out signature */ - if (X509PrintSignature(bio, x509, 0, 4) != WOLFSSL_SUCCESS) { + /* print out sig algo and signature */ + if (X509AcertPrintSignature(bio, x509, 0, 8) != WOLFSSL_SUCCESS) { return WOLFSSL_FAILURE; } @@ -6960,10 +7649,7 @@ int wolfSSL_X509_print_ex(WOLFSSL_BIO* bio, WOLFSSL_X509* x509, return WOLFSSL_SUCCESS; } -int wolfSSL_X509_print(WOLFSSL_BIO* bio, WOLFSSL_X509* x509) -{ - return wolfSSL_X509_print_ex(bio, x509, 0, 0); -} +#endif /* WOLFSSL_ACERT */ #ifndef NO_FILESYSTEM int wolfSSL_X509_print_fp(XFILE fp, WOLFSSL_X509 *x509) @@ -7699,6 +8385,95 @@ int wolfSSL_X509_REQ_verify(WOLFSSL_X509* x509, WOLFSSL_EVP_PKEY* pkey) } #endif /* WOLFSSL_CERT_REQ */ +#if defined(WOLFSSL_ACERT) + +#ifndef NO_WOLFSSL_STUB +WOLFSSL_API int wolfSSL_X509_ACERT_sign(WOLFSSL_X509_ACERT * x509, + WOLFSSL_EVP_PKEY * pkey, + const WOLFSSL_EVP_MD * md) +{ + WOLFSSL_STUB("X509_ACERT_sign"); + (void) x509; + (void) pkey; + (void) md; + return WOLFSSL_NOT_IMPLEMENTED; +} +#endif /* NO_WOLFSSL_STUB */ + +/* Helper function for ACERT_verify. + * + * @param [in] x509 the x509 attribute certificate + * @param [in, out] outSz the x509 der length + * + * @return der buffer on success + * @return NULL on error + * */ +static const byte* acert_get_der(WOLFSSL_X509_ACERT * x509, int* outSz) +{ + if (x509 == NULL || x509->derCert == NULL || outSz == NULL) { + return NULL; + } + + *outSz = (int)x509->derCert->length; + return x509->derCert->buffer; +} + +/* Given an X509_ACERT and EVP_PKEY, verify the acert's signature. + * + * @param [in] x509 the x509 attribute certificate + * @param [in] pkey the evp_pkey + * + * @return WOLFSSL_SUCCESS on verify success + * @return < 0 on error + * */ +int wolfSSL_X509_ACERT_verify(WOLFSSL_X509_ACERT* x509, WOLFSSL_EVP_PKEY* pkey) +{ + int ret = 0; + const byte * der = NULL; + int derSz = 0; + int pkey_type; + + if (x509 == NULL || pkey == NULL) { + WOLFSSL_MSG("error: wolfSSL_X509_ACERT_verify: bad arg"); + return WOLFSSL_FATAL_ERROR; + } + + WOLFSSL_ENTER("wolfSSL_X509_ACERT_verify"); + + der = acert_get_der(x509, &derSz); + + if (der == NULL || derSz <= 0) { + WOLFSSL_MSG("error: wolfSSL_X509_ACERT_verify: get der failed"); + return WOLFSSL_FATAL_ERROR; + } + + switch (pkey->type) { + case EVP_PKEY_RSA: + pkey_type = RSAk; + break; + + case EVP_PKEY_EC: + pkey_type = ECDSAk; + break; + + case EVP_PKEY_DSA: + pkey_type = DSAk; + break; + + default: + WOLFSSL_MSG("error: wolfSSL_X509_ACERT_verify: unknown pkey type"); + return WOLFSSL_FATAL_ERROR; + } + + + ret = VerifyX509Acert(der, (word32)derSz, + (const byte *)pkey->pkey.ptr, pkey->pkey_sz, + pkey_type, x509->heap); + + return ret == 0 ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE; +} +#endif /* WOLFSSL_ACERT */ + #if !defined(NO_FILESYSTEM) static void *wolfSSL_d2i_X509_fp_ex(XFILE file, void **x509, int type) { @@ -11391,6 +12166,63 @@ int wolfSSL_i2d_X509_NAME(WOLFSSL_X509_NAME* name, unsigned char** out) } +#if defined(WOLFSSL_ACERT) + WOLFSSL_X509_ACERT *wolfSSL_PEM_read_bio_X509_ACERT(WOLFSSL_BIO *bp, + WOLFSSL_X509_ACERT **x, + wc_pem_password_cb *cb, + void *u) + { + WOLFSSL_X509_ACERT* x509 = NULL; +#if defined(WOLFSSL_PEM_TO_DER) || defined(WOLFSSL_DER_TO_PEM) + unsigned char * pem = NULL; + int pemSz; + + WOLFSSL_ENTER("wolfSSL_PEM_read_bio_X509_ACERT"); + + if (bp == NULL) { + WOLFSSL_LEAVE("wolfSSL_PEM_read_bio_X509_ACERT", BAD_FUNC_ARG); + return NULL; + } + + if ((pemSz = wolfSSL_BIO_get_len(bp)) <= 0) { + /* No certificate in buffer */ + WOLFSSL_ERROR(ASN_NO_PEM_HEADER); + return NULL; + } + + pem = (unsigned char*)XMALLOC(pemSz, 0, DYNAMIC_TYPE_PEM); + + if (pem == NULL) { + return NULL; + } + + XMEMSET(pem, 0, pemSz); + + if (wolfSSL_BIO_read(bp, pem, pemSz) != pemSz) { + XFREE(pem, NULL, DYNAMIC_TYPE_PEM); + return NULL; + } + + x509 = wolfSSL_X509_ACERT_load_certificate_buffer(pem, pemSz, + WOLFSSL_FILETYPE_PEM); + + if (x != NULL) { + *x = x509; + } + + XFREE(pem, NULL, DYNAMIC_TYPE_PEM); + +#endif /* WOLFSSL_PEM_TO_DER || WOLFSSL_DER_TO_PEM */ + (void)bp; + (void)x; + (void)cb; + (void)u; + + return x509; + + } +#endif /* WOLFSSL_ACERT */ + WOLFSSL_X509 *wolfSSL_PEM_read_bio_X509(WOLFSSL_BIO *bp, WOLFSSL_X509 **x, wc_pem_password_cb *cb, void *u) { diff --git a/tests/api.c b/tests/api.c index 742b9158b8..d647e5fa1c 100644 --- a/tests/api.c +++ b/tests/api.c @@ -13488,6 +13488,272 @@ static int test_wolfSSL_X509_verify(void) return EXPECT_RESULT(); } +#if defined(WOLFSSL_ACERT) && !defined(NO_CERTS) && !defined(NO_RSA) && \ + !defined(NO_FILESYSTEM) && defined(OPENSSL_EXTRA) +/* Given acert file and its pubkey file, read them and then + * attempt to verify signed acert. + * + * If expect_pass is true, then verification should pass. + * If expect_pass is false, then verification should fail. + * */ +static int do_acert_verify_test(const char * acert_file, + const char * pkey_file, + size_t expect_pass) +{ + X509_ACERT * x509 = NULL; + EVP_PKEY * pkey = NULL; + BIO * bp = NULL; + int verify_rc = 0; + + /* First read the attribute certificate. */ + bp = BIO_new_file(acert_file, "r"); + if (bp == NULL) { + return -1; + } + + x509 = PEM_read_bio_X509_ACERT(bp, NULL, NULL, NULL); + BIO_free(bp); + bp = NULL; + + if (x509 == NULL) { + return -1; + } + + /* Next read the associated pub key. */ + bp = BIO_new_file(pkey_file, "r"); + + if (bp == NULL) { + X509_ACERT_free(x509); + x509 = NULL; + return -1; + } + + pkey = PEM_read_bio_PUBKEY(bp, &pkey, NULL, NULL); + BIO_free(bp); + bp = NULL; + + if (pkey == NULL) { + X509_ACERT_free(x509); + x509 = NULL; + return -1; + } + + /* Finally, do verification. */ + verify_rc = X509_ACERT_verify(x509, pkey); + + X509_ACERT_free(x509); + x509 = NULL; + + EVP_PKEY_free(pkey); + pkey = NULL; + + if (expect_pass && verify_rc != 1) { + return -1; + } + + if (!expect_pass && verify_rc == 1) { + return -1; + } + + return 0; +} +#endif + +static int test_wolfSSL_X509_ACERT_verify(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_ACERT) && !defined(NO_CERTS) && !defined(NO_RSA) && \ + defined(WC_RSA_PSS) && !defined(NO_FILESYSTEM) && defined(OPENSSL_EXTRA) + /* Walk over list of signed ACERTs and their pubkeys. + * All should load and pass verification. */ + const char * acerts[4] = {"certs/acert/acert.pem", + "certs/acert/acert_ietf.pem", + "certs/acert/rsa_pss/acert.pem", + "certs/acert/rsa_pss/acert_ietf.pem"}; + const char * pkeys[4] = {"certs/acert/acert_pubkey.pem", + "certs/acert/acert_ietf_pubkey.pem", + "certs/acert/rsa_pss/acert_pubkey.pem", + "certs/acert/rsa_pss/acert_ietf_pubkey.pem"}; + int rc = 0; + size_t i = 0; + size_t j = 0; + + for (i = 0; i < 4; ++i) { + for (j = i; j < 4; ++j) { + rc = do_acert_verify_test(acerts[i], pkeys[j], i == j); + + if (rc) { + fprintf(stderr, "error: %s: i = %zu, j = %zu, rc = %d\n", + "do_acert_verify_test", i, j, rc); + break; + } + } + + if (rc) { break; } + } + + ExpectIntEQ(rc, 0); +#endif + return EXPECT_RESULT(); +} + +static int test_wolfSSL_X509_ACERT_misc_api(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_ACERT) && !defined(NO_CERTS) && !defined(NO_RSA) && \ + !defined(NO_FILESYSTEM) && defined(OPENSSL_EXTRA) + const char * acerts[4] = {"certs/acert/acert.pem", + "certs/acert/acert_ietf.pem", + "certs/acert/rsa_pss/acert.pem", + "certs/acert/rsa_pss/acert_ietf.pem"}; + int rc = 0; + X509_ACERT * x509 = NULL; + BIO * bp = NULL; + long ver_long = 0; + int ver = 0; + int nid = 0; + const byte * raw_attr = NULL; + word32 attr_len = 0; + size_t i = 0; + int buf_len = 0; + byte ietf_serial[] = {0x03, 0xb5, 0x90, 0x59, 0x02, + 0xa2, 0xaa, 0xb5, 0x40, 0x21, + 0x44, 0xb8, 0x2c, 0x4f, 0xd9, + 0x80, 0x1b, 0x5f, 0x57, 0xc2}; + + for (i = 0; i < 4; ++i) { + const char * acert_file = acerts[i]; + int is_rsa_pss = 0; + int is_ietf_acert = 0; + byte serial[64]; + int serial_len = sizeof(serial); + + XMEMSET(serial, 0, sizeof(serial)); + + is_rsa_pss = XSTRSTR(acert_file, "rsa_pss") != NULL ? 1 : 0; + is_ietf_acert = XSTRSTR(acert_file, "ietf.pem") != NULL ? 1 : 0; + + /* First read the attribute certificate. */ + bp = BIO_new_file(acert_file, "r"); + ExpectNotNull(bp); + + x509 = PEM_read_bio_X509_ACERT(bp, NULL, NULL, NULL); + ExpectNotNull(x509); + + /* We're done with the bio for now. */ + if (bp != NULL) { + BIO_free(bp); + bp = NULL; + } + + /* Check version and signature NID. */ + ver_long = X509_ACERT_get_version(x509); + ExpectIntEQ(ver_long, 1); + + ver = wolfSSL_X509_ACERT_version(x509); + ExpectIntEQ(ver, 2); + + nid = X509_ACERT_get_signature_nid(x509); + + if (is_rsa_pss) { + ExpectIntEQ(nid, NID_rsassaPss); + } + else { + ExpectIntEQ(nid, NID_sha256WithRSAEncryption); + } + + /* Get the serial number buffer. + * The ietf acert example has a 20 byte serial number. */ + rc = wolfSSL_X509_ACERT_get_serial_number(x509, serial, &serial_len); + ExpectIntEQ(rc, SSL_SUCCESS); + + if (is_ietf_acert) { + ExpectIntEQ(serial_len, 20); + ExpectIntEQ(XMEMCMP(serial, ietf_serial, sizeof(ietf_serial)), 0); + } + else { + ExpectIntEQ(serial_len, 1); + ExpectTrue(serial[0] == 0x01); + } + + /* Repeat the same but with null serial buffer. This is ok. */ + rc = wolfSSL_X509_ACERT_get_serial_number(x509, NULL, &serial_len); + ExpectIntEQ(rc, SSL_SUCCESS); + + if (is_ietf_acert) { + ExpectIntEQ(serial_len, 20); + } + else { + ExpectIntEQ(serial_len, 1); + ExpectTrue(serial[0] == 0x01); + } + + /* Get the attributes buffer. */ + rc = wolfSSL_X509_ACERT_get_attr_buf(x509, &raw_attr, &attr_len); + ExpectIntEQ(rc, SSL_SUCCESS); + + if (is_ietf_acert) { + /* This cert has a 65 byte attributes field. */ + ExpectNotNull(raw_attr); + ExpectIntEQ(attr_len, 65); + } + else { + /* This cert has a 237 byte attributes field. */ + ExpectNotNull(raw_attr); + ExpectIntEQ(attr_len, 237); + } + + /* Test printing acert to memory bio. */ + ExpectNotNull(bp = BIO_new(BIO_s_mem())); + rc = X509_ACERT_print(bp, x509); + ExpectIntEQ(rc, SSL_SUCCESS); + + /* Now do a bunch of invalid stuff with partially valid inputs. */ + rc = wolfSSL_X509_ACERT_get_attr_buf(x509, &raw_attr, NULL); + ExpectIntEQ(rc, BAD_FUNC_ARG); + + rc = wolfSSL_X509_ACERT_get_attr_buf(x509, NULL, &attr_len); + ExpectIntEQ(rc, BAD_FUNC_ARG); + + rc = wolfSSL_X509_ACERT_get_attr_buf(NULL, &raw_attr, &attr_len); + ExpectIntEQ(rc, BAD_FUNC_ARG); + + ver_long = X509_ACERT_get_version(NULL); + ExpectIntEQ(ver_long, 0); + + ver = wolfSSL_X509_ACERT_version(NULL); + ExpectIntEQ(ver, 0); + + rc = wolfSSL_X509_ACERT_get_signature(x509, NULL, NULL); + ExpectIntEQ(rc, WOLFSSL_FATAL_ERROR); + + rc = wolfSSL_X509_ACERT_get_signature(x509, NULL, &buf_len); + ExpectIntEQ(rc, SSL_SUCCESS); + ExpectIntEQ(buf_len, 256); + + rc = wolfSSL_X509_ACERT_get_serial_number(x509, serial, NULL); + ExpectIntEQ(rc, BAD_FUNC_ARG); + + rc = X509_ACERT_print(bp, NULL); + ExpectIntEQ(rc, WOLFSSL_FAILURE); + + rc = X509_ACERT_print(NULL, x509); + ExpectIntEQ(rc, WOLFSSL_FAILURE); + + /* Finally free the acert and bio, we're done with them. */ + if (x509 != NULL) { + X509_ACERT_free(x509); + x509 = NULL; + } + + if (bp != NULL) { + BIO_free(bp); + bp = NULL; + } + } +#endif + return EXPECT_RESULT(); +} #if !defined(NO_DH) && !defined(NO_AES) && defined(WOLFSSL_CERT_GEN) && \ defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \ @@ -95342,6 +95608,10 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_X509_max_name_constraints), TEST_DECL(test_wolfSSL_make_cert), + /* X509 ACERT tests */ + TEST_DECL(test_wolfSSL_X509_ACERT_verify), + TEST_DECL(test_wolfSSL_X509_ACERT_misc_api), + #ifndef NO_BIO TEST_DECL(test_wolfSSL_X509_INFO_multiple_info), TEST_DECL(test_wolfSSL_X509_INFO), diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 5fc1858b4e..3f76da8098 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -24641,6 +24641,10 @@ wcchar END_CERT = "-----END CERTIFICATE-----"; wcchar BEGIN_CERT_REQ = "-----BEGIN CERTIFICATE REQUEST-----"; wcchar END_CERT_REQ = "-----END CERTIFICATE REQUEST-----"; #endif +#if defined(WOLFSSL_ACERT) + wcchar BEGIN_ACERT = "-----BEGIN ATTRIBUTE CERTIFICATE-----"; + wcchar END_ACERT = "-----END ATTRIBUTE CERTIFICATE-----"; +#endif /* WOLFSSL_ACERT */ #ifndef NO_DH wcchar BEGIN_DH_PARAM = "-----BEGIN DH PARAMETERS-----"; wcchar END_DH_PARAM = "-----END DH PARAMETERS-----"; @@ -24787,6 +24791,13 @@ int wc_PemGetHeaderFooter(int type, const char** header, const char** footer) ret = 0; break; #endif + #if defined(WOLFSSL_ACERT) + case ACERT_TYPE: + if (header) *header = BEGIN_ACERT; + if (footer) *footer = END_ACERT; + ret = 0; + break; + #endif /* WOLFSSL_ACERT */ #ifndef NO_DSA case DSA_TYPE: case DSA_PRIVATEKEY_TYPE: @@ -40181,6 +40192,1007 @@ int wc_RsaPublicKeyDecodeRaw(const byte* n, word32 nSz, const byte* e, } #endif /* !NO_RSA && (!NO_BIG_INT || WOLFSSL_SP_MATH) */ +#if defined(WOLFSSL_ACERT) && defined(WOLFSSL_ASN_TEMPLATE) +/* Initialize decoded certificate object with buffer of DER encoding. + * + * @param [in, out] cert Decoded certificate object. + * @param [in] source Buffer containing DER encoded certificate. + * @param [in] inSz Size of DER data in buffer in bytes. + * @param [in] heap Dynamic memory hint. + */ +void InitDecodedAcert(DecodedAcert* acert, const byte* source, word32 inSz, + void* heap) +{ + if (acert == NULL) { + return; + } + + WOLFSSL_MSG("InitDecodedAcert"); + + XMEMSET(acert, 0, sizeof(DecodedAcert)); + acert->heap = heap; + acert->source = source; /* don't own */ + acert->maxIdx = inSz; /* can't go over this index */ + acert->heap = heap; + + InitSignatureCtx(&acert->sigCtx, heap, INVALID_DEVID); + + return; +} + +/* Free the decoded attribute cert object's dynamic data. + * + * @param [in, out] acert Attribute Decoded certificate object. + */ +void FreeDecodedAcert(DecodedAcert * acert) +{ + if (acert == NULL) { + return; + } + + WOLFSSL_MSG("FreeDecodedAcert"); + + if (acert->holderIssuerName) { + FreeAltNames(acert->holderIssuerName, acert->heap); + acert->holderIssuerName = NULL; + } + + if (acert->holderEntityName) { + FreeAltNames(acert->holderEntityName, acert->heap); + acert->holderEntityName = NULL; + } + + if (acert->AttCertIssuerName) { + FreeAltNames(acert->AttCertIssuerName, acert->heap); + acert->AttCertIssuerName = NULL; + } + + FreeSignatureCtx(&acert->sigCtx); + + XMEMSET(acert, 0, sizeof(DecodedAcert)); + return; +} + +/* Decode an Attribute Cert GeneralName field. + * + * @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] len Length of data in buffer. + * @param [in] cert Decoded attribute certificate object. + * @param [in, out] entries Linked list of DNS name entries. + * + * @return 0 on success. + * @return ASN_PARSE_E when BER encoded data does not match ASN.1 items or + * is invalid. + * @return BUFFER_E when data in buffer is too small. + * @return ASN_UNKNOWN_OID_E when the OID cannot be verified. + * @return MEMORY_E when dynamic memory allocation fails. + */ +static int DecodeAcertGeneralName(const byte* input, word32* inOutIdx, + byte tag, int len, DecodedAcert* acert, + DNS_entry** entries) +{ + int ret = 0; + word32 idx = *inOutIdx; + + /* GeneralName choice: dnsName */ + if (tag == (ASN_CONTEXT_SPECIFIC | ASN_DNS_TYPE)) { + ret = SetDNSEntry(acert->heap, (const char*)(input + idx), len, + ASN_DNS_TYPE, entries); + if (ret == 0) { + idx += (word32)len; + } + } +#ifndef IGNORE_NAME_CONSTRAINTS + /* GeneralName choice: directoryName */ + else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_DIR_TYPE)) { + int strLen = 0; + word32 idxDir = idx; + + /* Expecting a SEQUENCE using up all data. */ + if (GetASN_Sequence(input, &idxDir, &strLen, idx + (word32)len, 1) < 0) + { + WOLFSSL_MSG("\tfail: seq length"); + return ASN_PARSE_E; + } + + ret = SetDNSEntry(acert->heap, (const char*)(input + idxDir), strLen, + ASN_DIR_TYPE, entries); + if (ret == 0) { + idx += (word32)len; + } + } + /* GeneralName choice: rfc822Name */ + else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_RFC822_TYPE)) { + ret = SetDNSEntry(acert->heap, (const char*)(input + idx), len, + ASN_RFC822_TYPE, entries); + if (ret == 0) { + idx += (word32)len; + } + } + /* GeneralName choice: uniformResourceIdentifier */ + else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_URI_TYPE)) { + WOLFSSL_MSG("\tPutting URI into list but not using"); + + #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" + As per RFC 3986 Sec 4.3, an absolute URI is only required to contain + a scheme and hier-part. So the only strict requirement is a ':' + being present after the scheme. If a '/' is present as part of the + hier-part, it must come after the ':' (see RFC 3986 Sec 3). */ + { + int i = 0; + + /* skip past scheme (i.e http,ftp,...) finding first ':' char */ + for (i = 0; i < len; i++) { + if (input[idx + (word32)i] == ':') { + break; + } + if (input[idx + (word32)i] == '/') { + i = len; /* error, found relative path since '/' was + * encountered before ':'. Returning error + * value in next if statement. */ + } + } + + /* test hier-part is empty */ + if (i == 0 || i == len) { + WOLFSSL_MSG("\tEmpty or malformed URI"); + WOLFSSL_ERROR_VERBOSE(ASN_ALT_NAME_E); + return ASN_ALT_NAME_E; + } + + /* test if scheme is missing */ + if (input[idx + (word32)i] != ':') { + WOLFSSL_MSG("\tAlt Name must be absolute URI"); + WOLFSSL_ERROR_VERBOSE(ASN_ALT_NAME_E); + return ASN_ALT_NAME_E; + } + } + #endif + + ret = SetDNSEntry(acert->heap, (const char*)(input + idx), len, + ASN_URI_TYPE, entries); + if (ret == 0) { + idx += (word32)len; + } + } + #if defined(WOLFSSL_QT) || defined(OPENSSL_ALL) || \ + defined(WOLFSSL_IP_ALT_NAME) + /* GeneralName choice: iPAddress */ + else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_IP_TYPE)) { + ret = SetDNSEntry(acert->heap, (const char*)(input + idx), len, + ASN_IP_TYPE, entries); + if (ret == 0) { + idx += (word32)len; + } + } + #endif /* WOLFSSL_QT || OPENSSL_ALL */ + + #ifdef OPENSSL_ALL + /* GeneralName choice: registeredID */ + else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_RID_TYPE)) { + ret = SetDNSEntry(acert->heap, (const char*)(input + idx), len, + ASN_RID_TYPE, entries); + if (ret == 0) { + idx += (word32)len; + } + } + #endif +#endif /* IGNORE_NAME_CONSTRAINTS */ + /* GeneralName choice: dNSName, x400Address, ediPartyName */ + else { + WOLFSSL_MSG("\tUnsupported name type, skipping"); + idx += (word32)len; + } + + if (ret == 0) { + /* Return index of next encoded byte. */ + *inOutIdx = idx; + } + return ret; +} + +/* Decode General Names from an ACERT input. + * + * @param [in] input Buffer holding encoded data. + * @param [in] sz Size of encoded data in bytes. + * @param [in, out] cert Decoded certificate object. + * @param [in, out] entries Linked list of DNS name entries. + * + * @return 0 on success. + * @return ASN_PARSE_E when BER encoded data does not match ASN.1 items or + * is invalid. + * @return BUFFER_E when data in buffer is too small. + * @return ASN_UNKNOWN_OID_E when the OID cannot be verified. + * @return MEMORY_E when dynamic memory allocation fails. + */ +static int DecodeAcertGeneralNames(const byte* input, word32 sz, + DecodedAcert* acert, + DNS_entry** entries) +{ + word32 idx = 0; + int length = 0; + int ret = 0; + word32 numNames = 0; + + /* Get SEQUENCE and expect all data to be accounted for. */ + if (GetASN_Sequence(input, &idx, &length, sz, 1) != 0) { + WOLFSSL_MSG("\tBad Sequence"); + return ASN_PARSE_E; + } + + if (length == 0) { + /* There is supposed to be a non-empty sequence here. */ + WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E); + return ASN_PARSE_E; + } + + if ((word32)length + idx != sz) { + return ASN_PARSE_E; + } + + while ((ret == 0) && (idx < sz)) { + ASNGetData dataASN[altNameASN_Length]; + + numNames++; + if (numNames > WOLFSSL_MAX_ALT_NAMES) { + WOLFSSL_MSG("error: acert: too many subject alternative names"); + ret = ASN_ALT_NAME_E; + break; + } + + /* Clear dynamic data items. */ + XMEMSET(dataASN, 0, sizeof(dataASN)); + /* Parse GeneralName with the choices supported. */ + GetASN_Choice(&dataASN[ALTNAMEASN_IDX_GN], generalNameChoice); + /* Decode a GeneralName choice. */ + ret = GetASN_Items(altNameASN, dataASN, altNameASN_Length, 0, input, + &idx, sz); + + if (ret != 0) { + break; + } + + ret = DecodeAcertGeneralName(input, &idx, + dataASN[ALTNAMEASN_IDX_GN].tag, + (int)dataASN[ALTNAMEASN_IDX_GN].length, + acert, entries); + } + + return ret; +} + +/* Holder has three potential forms: + * Holder ::= SEQUENCE { + * baseCertificateID [0] IssuerSerial OPTIONAL, + * -- the issuer and serial number of + * -- the holder's Public Key Certificate + * entityName [1] GeneralNames OPTIONAL, + * -- the name of the claimant or role + * objectDigestInfo [2] ObjectDigestInfo OPTIONAL + * -- used to directly authenticate the holder, + * -- for example, an executable + * } + * + * where IssuerSerial is: + * IssuerSerial ::= SEQUENCE { + * issuer GeneralNames, + * serial CertificateSerialNumber, + * issuerUID UniqueIdentifier OPTIONAL + * } + * + * Note: + * - Holder Option 2 objectDigestInfo is not mandatory + * for the spec and is not implemented here yet. + * + * - issuerUniqueID not supported yet. + * */ +static const ASNItem HolderASN[] = +{ + /* Holder root sequence. */ +/* HOLDER_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 }, + /* Holder Option 0:*/ +/* ISSUERSERIAL_SEQ */ { 1, ASN_CONTEXT_SPECIFIC | 0, 1, 1, 2 }, + /* issuer GeneralNames, */ +/* GN_SEQ */ { 2, ASN_SEQUENCE, 1, 0, 0 }, + /* serial CertificateSerialNumber */ +/* SERIAL_INT */ { 2, ASN_INTEGER, 0, 0, 0 }, + /* Holder Option 1:*/ +/* GN_SEQ */ { 1, ASN_CONTEXT_SPECIFIC | 1, 1, 0, 2 }, +}; + +enum { + HOLDER_IDX_SEQ = 0, + HOLDER_IDX_ISSUERSERIAL_SEQ, + HOLDER_IDX_GN_SEQ, + HOLDER_IDX_SERIAL_INT, + HOLDER_IDX_GN_SEQ_OPT1, +}; + +/* Number of items in ASN template for an X509 Acert. */ +#define HolderASN_Length (sizeof(HolderASN) / sizeof(ASNItem)) + +/* Decode the Holder field of an x509 attribute certificate. + * + * + * @param [in] input Buffer containing encoded Holder field. + * @param [in] len Length of Holder field. + * @param [in] cert Decoded certificate object. + * + * @return 0 on success. + * @return ASN_PARSE_E when BER encoded data does not match ASN.1 items or + * is invalid. + * @return BUFFER_E when data in buffer is too small. + * @return ASN_UNKNOWN_OID_E when the OID cannot be verified. + * @return MEMORY_E when dynamic memory allocation fails. + * */ +static int DecodeHolder(const byte* input, word32 len, DecodedAcert* acert) +{ + DECL_ASNGETDATA(dataASN, HolderASN_Length); + int ret = 0; + word32 idx = 0; + word32 holderSerialSz = 0; + + if (input == NULL || len <= 0 || acert == NULL) { + return BUFFER_E; + } + + CALLOC_ASNGETDATA(dataASN, HolderASN_Length, ret, acert->heap); + + if (ret != 0) { + FREE_ASNGETDATA(dataASN, acert->heap); + return MEMORY_E; + } + + holderSerialSz = EXTERNAL_SERIAL_SIZE; + + GetASN_Buffer(&dataASN[HOLDER_IDX_SERIAL_INT], acert->holderSerial, + &holderSerialSz); + + ret = GetASN_Items(HolderASN, dataASN, HolderASN_Length, 0, input, + &idx, len); + + if (ret != 0) { + WOLFSSL_MSG("error: Holder: GetASN_Items failed"); + FREE_ASNGETDATA(dataASN, acert->heap); + return ret; + } + + if (dataASN[HOLDER_IDX_SERIAL_INT].tag != 0) { + acert->holderSerialSz = (int)holderSerialSz; + } + else { + acert->holderSerialSz = 0; + } + + { + /* Now parse the GeneralNames field. + * Use the HOLDER_IDX_GN_SEQ offset for input. */ + const byte * gn_input = NULL; + word32 gn_len = 0; + word32 holder_index = HOLDER_IDX_GN_SEQ; + + /* Determine which tag was seen. */ + if (dataASN[HOLDER_IDX_GN_SEQ].tag != 0) { + gn_input = input + dataASN[holder_index].offset; + gn_len = dataASN[holder_index].length + 2; + } + else { + gn_input = input; + gn_len = len; + } + + ret = DecodeAcertGeneralNames(gn_input, gn_len, acert, + &acert->holderIssuerName); + + if (ret != 0) { + WOLFSSL_MSG("error: Holder: DecodeAcertGeneralNames failed"); + FREE_ASNGETDATA(dataASN, acert->heap); + return ret; + } + } + + FREE_ASNGETDATA(dataASN, acert->heap); + return 0; +} + +/* From RFC 5755. + * 4.2.3. Issuer + * + * ACs conforming to this profile MUST use the v2Form choice, which MUST + * contain one and only one GeneralName in the issuerName, which MUST + * contain a non-empty distinguished name in the directoryName field. + * This means that all AC issuers MUST have non-empty distinguished + * names. ACs conforming to this profile MUST omit the + * baseCertificateID and objectDigestInfo fields. + * + * 4.1. X.509 Attribute Certificate Definition + * + * AttCertIssuer ::= CHOICE { + * v1Form GeneralNames, -- MUST NOT be used in this + * -- profile + * v2Form [0] V2Form -- v2 only + * } + * + * V2Form ::= SEQUENCE { + * issuerName GeneralNames OPTIONAL, + * baseCertificateID [0] IssuerSerial OPTIONAL, + * objectDigestInfo [1] ObjectDigestInfo OPTIONAL + * -- issuerName MUST be present in this profile + * -- baseCertificateID and objectDigestInfo MUST + * -- NOT be present in this profile + * } + * */ +static const ASNItem AttCertIssuerASN[] = +{ + /* V2Form ::= SEQUENCE { */ +/* AttCertIssuer_GN_SEQ */ { 0, ASN_SEQUENCE, 1, 0, 0 }, +}; + +enum { + ATTCERTISSUER_IDX_GN_SEQ, +}; + +/* Number of items in ASN template for an X509 Acert. */ +#define AttCertIssuerASN_Length (sizeof(AttCertIssuerASN) / sizeof(ASNItem)) + +/* Decode the AttCertIssuer Field of an x509 attribute certificate. + * + * + * @param [in] input Buffer containing encoded AttCertIssuer field. + * @param [in] len Length of Holder field. + * @param [in] cert Decoded certificate object. + * + * @return 0 on success. + * @return ASN_PARSE_E when BER encoded data does not match ASN.1 items or + * is invalid. + * @return BUFFER_E when data in buffer is too small. + * @return ASN_UNKNOWN_OID_E when the OID cannot be verified. + * @return MEMORY_E when dynamic memory allocation fails. + * */ +static int DecodeAttCertIssuer(const byte* input, word32 len, + DecodedAcert* cert) +{ + DECL_ASNGETDATA(dataASN, AttCertIssuerASN_Length); + int ret = 0; + word32 idx = 0; + const byte * gn_input = NULL; + word32 gn_len = 0; + + if (input == NULL || len <= 0 || cert == NULL) { + return BUFFER_E; + } + + CALLOC_ASNGETDATA(dataASN, AttCertIssuerASN_Length, ret, cert->heap); + + if (ret != 0) { + return MEMORY_E; + } + + ret = GetASN_Items(AttCertIssuerASN, dataASN, AttCertIssuerASN_Length, + 0, input, &idx, len); + + if (ret != 0) { + FREE_ASNGETDATA(dataASN, cert->heap); + WOLFSSL_MSG("error: AttCertIssuer: GetASN_Items failed"); + return ret; + } + + /* Now parse the GeneralNames field. + * Use the HOLDER_IDX_GN_SEQ offset for input. */ + gn_input = input + dataASN[ATTCERTISSUER_IDX_GN_SEQ].offset; + gn_len = dataASN[ATTCERTISSUER_IDX_GN_SEQ].length + 2; + + ret = DecodeAcertGeneralNames(gn_input, gn_len, cert, + &cert->AttCertIssuerName); + + if (ret != 0) { + FREE_ASNGETDATA(dataASN, cert->heap); + WOLFSSL_MSG("error: AttCertIssuer: DecodeAcertGeneralNames failed"); + return ret; + } + + FREE_ASNGETDATA(dataASN, cert->heap); + return 0; +} + + +/* ASN template for an X509 Attribute Certificate, + * from RFC 5755 + */ +static const ASNItem AcertASN[] = +{ + /* AttributeCertificate ::= SEQUENCE */ +/* SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 }, + /* AttributeCertificateInfo ::= SEQUENCE */ +/* ACINFO_SEQ */ { 1, ASN_SEQUENCE, 1, 1, 0 }, + /* AttCertVersion ::= INTEGER { v2(1) } */ +/* ACINFO_VER_INT */ { 2, ASN_INTEGER, 0, 0, 0 }, + /* holder Holder */ +/* ACINFO_HOLDER_SEQ */ { 2, ASN_SEQUENCE, 1, 0, 0 }, + /* issuer AttCertIssuer */ +/* ACINFO_CHOICE_SEQ */ { 2, ASN_CONTEXT_SPECIFIC | 0, 1, 0, 2 }, +/* ACINFO_ISSUER_SEQ */ { 2, ASN_SEQUENCE | 0, 1, 0, 2 }, + /* signature AlgorithmIdentifier */ + /* AlgorithmIdentifier ::= SEQUENCE */ +/* ACINFO_ALGOID_SEQ */ { 2, ASN_SEQUENCE, 1, 1, 0 }, + /* Algorithm OBJECT IDENTIFIER */ +/* ACINFO_ALGOID_OID */ { 3, ASN_OBJECT_ID, 0, 0, 0 }, + /* parameters */ +/* ACINFO_ALGOID_PARAMS_NULL */ { 3, ASN_TAG_NULL, 0, 0, 2 }, +#ifdef WC_RSA_PSS +/* ACINFO_ALGOID_PARAMS */ { 3, ASN_SEQUENCE, 1, 0, 2 }, +#endif + /* CertificateSerialNumber ::= INTEGER */ +/* ACINFO_SERIAL */ { 2, ASN_INTEGER, 0, 0, 0 }, + /* Validity ::= SEQUENCE */ +/* ACINFO_VALIDITY_SEQ */ { 2, ASN_SEQUENCE, 1, 1, 0 }, + /* notBeforeTime GeneralizedTime, */ +/* ACINFO_VALIDITY_NOTB_GT */ { 3, ASN_GENERALIZED_TIME, 0, 0, 2 }, + /* notAfterTime GeneralizedTime */ +/* ACINFO_VALIDITY_NOTA_GT */ { 3, ASN_GENERALIZED_TIME, 0, 0, 3 }, + /* attributes SEQUENCE OF Attribute */ +/* ACINFO_ATTRIBUTES_SEQ */ { 2, ASN_SEQUENCE, 1, 0, 0 }, + /* issuerUniqueID OPTIONAL, */ +/* ACINFO_UNIQUE_ID */ { 2, ASN_CONTEXT_SPECIFIC | 1, 0, 0, 1 }, + /* extensions OPTIONAL */ +/* ACINFO_EXT */ { 2, ASN_CONTEXT_SPECIFIC | 2, 1, 1, 1 }, +/* ACINFO_EXT_SEQ */ { 2, ASN_SEQUENCE, 1, 0, 1 }, + /* signature AlgorithmIdentifier */ + /* AlgorithmIdentifier ::= SEQUENCE */ +/* SIGALGO_SEQ */ { 1, ASN_SEQUENCE, 1, 1, 0 }, + /* Algorithm OBJECT IDENTIFIER */ +/* SIGALGO_OID */ { 2, ASN_OBJECT_ID, 0, 0, 0 }, + /* parameters */ +/* SIGALGO_PARAMS_NULL */ { 2, ASN_TAG_NULL, 0, 0, 2 }, +#ifdef WC_RSA_PSS +/* SIGALGO_PARAMS */ { 2, ASN_SEQUENCE, 1, 0, 2 }, +#endif + /* signature BIT STRING */ +/* SIGNATURE */ { 1, ASN_BIT_STRING, 0, 0, 0 }, +}; + +enum { + ACERT_IDX_SEQ = 0, + ACERT_IDX_ACINFO_SEQ, + ACERT_IDX_ACINFO_VER_INT, + /* ACINFO holder and issuer */ + ACERT_IDX_ACINFO_HOLDER_SEQ, + ACERT_IDX_ACINFO_CHOICE_SEQ, + ACERT_IDX_ACINFO_ISSUER_SEQ, + /* ACINFO sig alg*/ + ACERT_IDX_ACINFO_ALGOID_SEQ, + ACERT_IDX_ACINFO_ALGOID_OID, + ACERT_IDX_ACINFO_ALGOID_PARAMS_NULL, +#ifdef WC_RSA_PSS + /* Additional RSA-PSS params. */ + ACERT_IDX_ACINFO_ALGOID_PARAMS, +#endif + /* serial number */ + ACERT_IDX_ACINFO_SERIAL, + /* validity time */ + ACERT_IDX_ACINFO_VALIDITY_SEQ, + ACERT_IDX_ACINFO_VALIDITY_NOTB_GT, + ACERT_IDX_ACINFO_VALIDITY_NOTA_GT, + /* attributes */ + ACERT_IDX_ACINFO_ATTRIBUTES_SEQ, + /* unique identifier */ + ACERT_IDX_ACINFO_UNIQUE_ID, + /* extensions */ + ACERT_ACINFO_EXT, + ACERT_ACINFO_EXT_SEQ, + /* sig alg */ + ACERT_IDX_SIGALGO_SEQ, + ACERT_IDX_SIGALGO_OID, + ACERT_IDX_SIGALGO_PARAMS_NULL, +#ifdef WC_RSA_PSS + /* Additional RSA-PSS params. */ + ACERT_IDX_SIGALGO_PARAMS, +#endif + /* signature */ + ACERT_IDX_SIGNATURE, + WOLF_ENUM_DUMMY_LAST_ELEMENT(ACERT_IDX) +}; + +/* Number of items in ASN template for an X509 Acert. */ +#define AcertASN_Length (sizeof(AcertASN) / sizeof(ASNItem)) + +/* Initial implementation for parsing and verifying an + * X509 Attribute Certificate (RFC 5755). + * + * At present these fields are NOT parsed: + * - issuerUniqueID + * - extensions + * - attributes + * + * Returns 0 on success. + * Returns negative error code on error/failure. + * */ +int ParseX509Acert(DecodedAcert* acert, int verify) +{ + DECL_ASNGETDATA(dataASN, AcertASN_Length); + int ret = 0; + word32 idx = 0; + int badDate = 0; + byte version = 0; + word32 serialSz = EXTERNAL_SERIAL_SIZE; + + if (acert == NULL) { + return BAD_FUNC_ARG; + } + + CALLOC_ASNGETDATA(dataASN, AcertASN_Length, ret, acert->heap); + + if (ret != 0) { + return MEMORY_E; + } + + /* Get the version and put the serial number into the buffer. */ + GetASN_Int8Bit(&dataASN[ACERT_IDX_ACINFO_VER_INT], &version); + + GetASN_Buffer(&dataASN[ACERT_IDX_ACINFO_SERIAL], acert->serial, + &serialSz); + + /* Check OID types for signature algorithm. */ + GetASN_OID(&dataASN[ACERT_IDX_ACINFO_ALGOID_OID], oidSigType); + GetASN_OID(&dataASN[ACERT_IDX_SIGALGO_OID], oidSigType); + + /* Parse the X509 certificate. */ + ret = GetASN_Items(AcertASN, dataASN, AcertASN_Length, 1, + acert->source, &acert->srcIdx, acert->maxIdx); + + if (ret != 0) { + FREE_ASNGETDATA(dataASN, acert->heap); + return ret; + } + + /* Check version is valid/supported - can't be negative. */ + if (version > MAX_X509_VERSION) { + FREE_ASNGETDATA(dataASN, acert->heap); + WOLFSSL_MSG("Unexpected attribute certificate version"); + WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E); + return ASN_PARSE_E; + } + + acert->version = version; + acert->serialSz = (int)serialSz; + + acert->signatureOID = dataASN[ACERT_IDX_ACINFO_ALGOID_OID].data.oid.sum; + acert->certBegin = dataASN[ACERT_IDX_ACINFO_SEQ].offset; + + /* check BEFORE date. */ + idx = ACERT_IDX_ACINFO_VALIDITY_NOTB_GT; + if (CheckDate(&dataASN[idx], BEFORE) < 0) { + if ((verify != NO_VERIFY) && (verify != VERIFY_SKIP_DATE)) { + badDate = ASN_BEFORE_DATE_E; + } + } + + /* Store reference to BEFORE date. */ + acert->beforeDate = GetASNItem_Addr(dataASN[idx], acert->source); + acert->beforeDateLen = (int)GetASNItem_Length(dataASN[idx], acert->source); + + /* check AFTER date. */ + idx = ACERT_IDX_ACINFO_VALIDITY_NOTA_GT; + if (CheckDate(&dataASN[idx], AFTER) < 0) { + if ((verify != NO_VERIFY) && (verify != VERIFY_SKIP_DATE)) { + badDate = ASN_BEFORE_DATE_E; + } + } + + /* Store reference to AFTER date. */ + acert->afterDate = GetASNItem_Addr(dataASN[idx], acert->source); + acert->afterDateLen = (int)GetASNItem_Length(dataASN[idx], acert->source); + + /* Store the signature information. */ + acert->sigIndex = dataASN[ACERT_IDX_SIGALGO_SEQ].offset; + GetASN_GetConstRef(&dataASN[ACERT_IDX_SIGNATURE], + &acert->signature, &acert->sigLength); + + /* Make sure 'signature' and 'signatureAlgorithm' are the same. */ + if (dataASN[ACERT_IDX_SIGALGO_OID].data.oid.sum != acert->signatureOID) { + FREE_ASNGETDATA(dataASN, acert->heap); + WOLFSSL_ERROR_VERBOSE(ASN_SIG_OID_E); + return ASN_SIG_OID_E; + } + + /* Parameters not allowed after ECDSA or EdDSA algorithm OID. */ + if (IsSigAlgoECC(acert->signatureOID)) { + if ((dataASN[ACERT_IDX_SIGALGO_PARAMS_NULL].tag != 0) + #ifdef WC_RSA_PSS + || (dataASN[ACERT_IDX_SIGALGO_PARAMS].tag != 0) + #endif + ) { + FREE_ASNGETDATA(dataASN, acert->heap); + WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E); + return ASN_PARSE_E; + } + } + + #ifdef WC_RSA_PSS + /* Check parameters starting with a SEQUENCE. */ + if (dataASN[ACERT_IDX_SIGALGO_PARAMS].tag != 0) { + word32 oid = dataASN[ACERT_IDX_SIGALGO_OID].data.oid.sum; + word32 sigAlgParamsSz = 0; + const byte * acParams = NULL; + word32 acParamsSz = 0; + const byte * sigAlgParams = NULL; + + /* Parameters only with RSA PSS. */ + if (oid != CTC_RSASSAPSS) { + FREE_ASNGETDATA(dataASN, acert->heap); + WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E); + return ASN_PARSE_E; + } + + /* Check RSA PSS parameters are the same. */ + acParams = GetASNItem_Addr(dataASN[ACERT_IDX_ACINFO_ALGOID_PARAMS], + acert->source); + acParamsSz = GetASNItem_Length(dataASN[ACERT_IDX_ACINFO_ALGOID_PARAMS], + acert->source); + sigAlgParams = GetASNItem_Addr(dataASN[ACERT_IDX_SIGALGO_PARAMS], + acert->source); + sigAlgParamsSz = GetASNItem_Length(dataASN[ACERT_IDX_SIGALGO_PARAMS], + acert->source); + + if ((acParamsSz != sigAlgParamsSz) || + (XMEMCMP(acParams, sigAlgParams, acParamsSz) != 0)) { + + FREE_ASNGETDATA(dataASN, acert->heap); + WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E); + return ASN_PARSE_E; + } + + /* Store RSA PSS parameters for use in signature verification. */ + acert->sigParamsIndex = dataASN[ACERT_IDX_SIGALGO_PARAMS].offset; + acert->sigParamsLength = sigAlgParamsSz; + } + #endif + + /* Store the raw Attributes field. */ + GetASN_GetConstRef(&dataASN[ACERT_IDX_ACINFO_ATTRIBUTES_SEQ], + &acert->rawAttr, &acert->rawAttrLen); + + { + /* Now parse the Holder and AttCertIssuer fields. + * Use the ACINFO holder and issuer sequence offset for input. */ + const byte * holder_input = NULL; + word32 holder_len = 0; + const byte * issuer_input = NULL; + word32 issuer_len = 0; + word32 i_holder = ACERT_IDX_ACINFO_HOLDER_SEQ; + word32 i_issuer = 0; + + /* Determine which issuer tag was seen. We need this to determine + * the holder_input. */ + i_issuer = (dataASN[ACERT_IDX_ACINFO_CHOICE_SEQ].tag != 0) ? + ACERT_IDX_ACINFO_CHOICE_SEQ : ACERT_IDX_ACINFO_ISSUER_SEQ; + + holder_input = acert->source + dataASN[i_holder].offset; + holder_len = dataASN[i_issuer].offset - dataASN[i_holder].offset; + + ret = DecodeHolder(holder_input, holder_len, acert); + + if (ret != 0) { + FREE_ASNGETDATA(dataASN, acert->heap); + return ret; + } + + #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE + printf("debug: parse acert:issuer index: %d\n", i_issuer); + #endif /* WOLFSSL_DEBUG_ASN_TEMPLATE */ + + GetASN_GetConstRef(&dataASN[i_issuer], &issuer_input, &issuer_len); + + if (i_issuer == ACERT_IDX_ACINFO_CHOICE_SEQ && issuer_len > 0) { + /* Try to decode the AttCertIssuer as well. */ + ret = DecodeAttCertIssuer(issuer_input, issuer_len, acert); + + if (ret != 0) { + FREE_ASNGETDATA(dataASN, acert->heap); + return ret; + } + } + #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE + else { + printf("debug: parse acert: unsupported issuer format: %d, %d\n", + i_issuer, issuer_len); + } + #endif /* WOLFSSL_DEBUG_ASN_TEMPLATE */ + } + + if (badDate) { + if ((verify != NO_VERIFY) && (verify != VERIFY_SKIP_DATE)) { + ret = badDate; + } + } + + FREE_ASNGETDATA(dataASN, acert->heap); + return ret; +} + +/* Given the parsed attribute cert info, verify the signature. + * + * The sigCtx is alloced and freed here. + * + * @param [in] acinfo the parsed acinfo sequence + * @param [in] acinfoSz the parsed acinfo sequence length + * @param [in] pubKey public key + * @param [in] pubKeySz public key length + * @param [in] pubKeyOID public key oid + * @param [in] sig the parsed signature + * @param [in] sigSz the parsed signature length + * @param [in] sigOID the parsed signature OID + * @param [in] sigParams the parsed signature RSA-PSS params + * @param [in] sigParamsSz the parsed signature RSA-PSS params length + * @param [in] heap heap hint + * + * @return 0 on verify success + * @return < 0 on error + * */ +static int acert_sig_verify(const byte * acinfo, word32 acinfoSz, + const byte * pubKey, word32 pubKeySz, + int pubKeyOID, const byte * sig, word32 sigSz, + word32 sigOID, const byte * sigParams, + word32 sigParamsSz, void * heap) +{ +#ifndef WOLFSSL_SMALL_STACK + SignatureCtx sigCtx[1]; +#else + SignatureCtx * sigCtx = NULL; +#endif + int ret = 0; + + #ifdef WOLFSSL_SMALL_STACK + sigCtx = (SignatureCtx*)XMALLOC(sizeof(*sigCtx), heap, + DYNAMIC_TYPE_SIGNATURE); + if (sigCtx == NULL) { + WOLFSSL_MSG("error: VerifyX509Acert: malloc sigCtx failed"); + return MEMORY_E; + } + #endif + + InitSignatureCtx(sigCtx, heap, INVALID_DEVID); + + /* Check x509 acert signature. */ + ret = ConfirmSignature(sigCtx, acinfo, acinfoSz, pubKey, pubKeySz, + (word32)pubKeyOID, sig, sigSz, sigOID, + sigParams, sigParamsSz, NULL); + + if (ret == WC_NO_ERR_TRACE(ASN_SIG_CONFIRM_E)) { + WOLFSSL_MSG("info: VerifyX509Acert: confirm signature failed"); + } + + FreeSignatureCtx(sigCtx); + #ifdef WOLFSSL_SMALL_STACK + XFREE(sigCtx, heap, DYNAMIC_TYPE_SIGNATURE); + sigCtx = NULL; + #endif + + return ret; +} + +/* Verify the X509 ACERT signature, using the given pubkey. + * + * @param [in] der input acert in der format + * @param [in] derSz acert length + * @param [in] pubKey public key + * @param [in] pubKeySz public key length + * @param [in] pubKeyOID public key oid + * @param [in] heap heap hint + * + * @return 0 on success + * @return < 0 on error + * */ +int VerifyX509Acert(const byte* der, word32 derSz, + const byte* pubKey, word32 pubKeySz, int pubKeyOID, + void * heap) +{ + DECL_ASNGETDATA(dataASN, AcertASN_Length); + word32 idx = 0; + int ret = 0; + const byte * acinfo = NULL; /* The acinfo sequence. */ + word32 acinfoSz = 0; /* The acinfo sequence length. */ +#ifdef WC_RSA_PSS + const byte * acParams = NULL; + word32 acParamsSz = 0; +#endif + const byte * sig = NULL; + word32 sigSz = 0; + word32 sigOID = 0; + const byte * sigParams = NULL; + word32 sigParamsSz = 0; + + if (der == NULL || pubKey == NULL || derSz == 0 || pubKeySz == 0) { + WOLFSSL_MSG("error: VerifyX509Acert: bad args"); + return BAD_FUNC_ARG; + } + + CALLOC_ASNGETDATA(dataASN, AcertASN_Length, ret, heap); + + if (ret != 0) { + WOLFSSL_MSG("error: VerifyX509Acert: calloc dataASN failed"); + return MEMORY_E; + } + + /* Check OID types for signature algorithm. */ + GetASN_OID(&dataASN[ACERT_IDX_ACINFO_ALGOID_OID], oidSigType); + GetASN_OID(&dataASN[ACERT_IDX_SIGALGO_OID], oidSigType); + + /* Parse the X509 certificate. */ + ret = GetASN_Items(AcertASN, dataASN, AcertASN_Length, 1, + der, &idx, derSz); + + if (ret != 0) { + WOLFSSL_MSG("error: VerifyX509Acert: GetASN_Items failed"); + FREE_ASNGETDATA(dataASN, heap); + return ret; + } + + /* Check signature OIDs match. */ + if (dataASN[ACERT_IDX_ACINFO_ALGOID_OID].data.oid.sum + != dataASN[ACERT_IDX_SIGALGO_OID].data.oid.sum) { + WOLFSSL_MSG("error: VerifyX509Acert: sig OID mismatch"); + FREE_ASNGETDATA(dataASN, heap); + return ASN_SIG_OID_E; + } + + /* Get the attribute certificate info. */ + acinfo = GetASNItem_Addr(dataASN[ACERT_IDX_ACINFO_SEQ], der); + acinfoSz = GetASNItem_Length(dataASN[ACERT_IDX_ACINFO_SEQ], der); + + if (acinfo == NULL || acinfoSz == 0) { + WOLFSSL_MSG("error: VerifyX509Acert: empty acinfo"); + FREE_ASNGETDATA(dataASN, heap); + return ASN_PARSE_E; + } + + /* Get acert signature and sig info. */ + sigOID = dataASN[ACERT_IDX_ACINFO_ALGOID_OID].data.oid.sum; + #ifdef WC_RSA_PSS + if (dataASN[ACERT_IDX_ACINFO_ALGOID_PARAMS].tag != 0) { + acParams = GetASNItem_Addr(dataASN[ACERT_IDX_ACINFO_ALGOID_PARAMS], + der); + acParamsSz = GetASNItem_Length(dataASN[ACERT_IDX_ACINFO_ALGOID_PARAMS], + der); + } + if (dataASN[ACERT_IDX_SIGALGO_PARAMS].tag != 0) { + sigParams = GetASNItem_Addr(dataASN[ACERT_IDX_SIGALGO_PARAMS], der); + sigParamsSz = GetASNItem_Length(dataASN[ACERT_IDX_SIGALGO_PARAMS], + der); + } + #endif + + GetASN_GetConstRef(&dataASN[ACERT_IDX_SIGNATURE], &sig, &sigSz); + + #ifdef WC_RSA_PSS + if (acParamsSz != sigParamsSz) { + ret = ASN_PARSE_E; + } + else if ((acParamsSz > 0) && (sigOID != CTC_RSASSAPSS)) { + ret = ASN_PARSE_E; + } + else if ((acParamsSz > 0) && + (XMEMCMP(acParams, sigParams, acParamsSz) != 0)) { + ret = ASN_PARSE_E; + } + #endif + + if (ret == 0) { + /* Finally, do the verification. */ + ret = acert_sig_verify(acinfo, acinfoSz, + pubKey, pubKeySz, pubKeyOID, + sig, sigSz, sigOID, sigParams, sigParamsSz, + heap); + } + + FREE_ASNGETDATA(dataASN, heap); + return ret; +} +#endif /* WOLFSSL_ACERT && WOLFSSL_ASN_TEMPLATE */ #ifdef WOLFSSL_SEP diff --git a/wolfssl/internal.h b/wolfssl/internal.h index f63440fcb6..a069e93938 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -5317,6 +5317,29 @@ struct WOLFSSL_X509 { #endif /* WOLFSSL_DUAL_ALG_CERTS */ }; +#if defined(WOLFSSL_ACERT) +struct WOLFSSL_X509_ACERT { + int version; + int serialSz; + byte serial[EXTERNAL_SERIAL_SIZE]; + WOLFSSL_ASN1_TIME notBefore; + WOLFSSL_ASN1_TIME notAfter; + buffer sig; + int sigOID; +#ifndef NO_CERTS + DerBuffer * derCert; +#endif + void* heap; + /* copy of raw Attributes field from */ + byte holderSerial[EXTERNAL_SERIAL_SIZE]; + int holderSerialSz; + DNS_entry * holderEntityName; /* Holder entityName from ACERT */ + DNS_entry * holderIssuerName; /* issuerName from ACERT */ + DNS_entry * AttCertIssuerName; /* AttCertIssuer name from ACERT */ + byte * rawAttr; + word32 rawAttrLen; +}; +#endif /* WOLFSSL_ACERT */ /* record layer header for PlainText, Compressed, and CipherText */ typedef struct RecordLayerHeader { @@ -6595,6 +6618,12 @@ WOLFSSL_LOCAL enum wc_HashType HashAlgoToType(int hashAlgo); DecodedCert* dCert); #endif +#if defined(WOLFSSL_ACERT) + WOLFSSL_LOCAL int CopyDecodedAcertToX509(WOLFSSL_X509_ACERT* x509, + DecodedAcert* dAcert); +#endif /* WOLFSSL_ACERT */ + + #ifndef MAX_CIPHER_NAME #define MAX_CIPHER_NAME 50 #endif diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index 7f8fad1a70..ef65f60ea9 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -100,6 +100,7 @@ typedef WOLFSSL_CIPHER SSL_CIPHER; typedef WOLFSSL_X509_LOOKUP X509_LOOKUP; typedef WOLFSSL_X509_LOOKUP_METHOD X509_LOOKUP_METHOD; typedef WOLFSSL_X509_CRL X509_CRL; +typedef WOLFSSL_X509_ACERT X509_ACERT; typedef WOLFSSL_X509_EXTENSION X509_EXTENSION; typedef WOLFSSL_X509_PUBKEY X509_PUBKEY; typedef WOLFSSL_X509_ALGOR X509_ALGOR; @@ -745,6 +746,14 @@ wolfSSL_X509_STORE_set_verify_cb((WOLFSSL_X509_STORE *)(s), (WOLFSSL_X509_STORE_ #define X509_CRL_get_version wolfSSL_X509_CRL_version #define X509_load_crl_file wolfSSL_X509_load_crl_file +#define X509_ACERT_free wolfSSL_X509_ACERT_free +#define X509_ACERT_get_version wolfSSL_X509_ACERT_get_version +#define X509_ACERT_get_signature_nid wolfSSL_X509_ACERT_get_signature_nid +#define X509_ACERT_print wolfSSL_X509_ACERT_print +#define X509_ACERT_verify wolfSSL_X509_ACERT_verify +#define X509_ACERT_sign wolfSSL_X509_ACERT_sign +#define PEM_read_bio_X509_ACERT wolfSSL_PEM_read_bio_X509_ACERT + #define X509_get_X509_PUBKEY wolfSSL_X509_get_X509_PUBKEY #define X509_REQ_get_X509_PUBKEY wolfSSL_X509_get_X509_PUBKEY #define X509_get0_tbs_sigalg wolfSSL_X509_get0_tbs_sigalg diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 63b2a8ed55..5788b2f142 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -149,6 +149,7 @@ typedef struct WOLFSSL_CTX WOLFSSL_CTX; typedef struct WOLFSSL_STACK WOLFSSL_STACK; typedef struct WOLFSSL_X509 WOLFSSL_X509; +typedef struct WOLFSSL_X509_ACERT WOLFSSL_X509_ACERT; typedef struct WOLFSSL_X509_NAME WOLFSSL_X509_NAME; typedef struct WOLFSSL_X509_NAME_ENTRY WOLFSSL_X509_NAME_ENTRY; typedef struct WOLFSSL_X509_PUBKEY WOLFSSL_X509_PUBKEY; @@ -2944,6 +2945,7 @@ WOLFSSL_API WOLFSSL_X509* wolfSSL_get_chain_X509(WOLFSSL_X509_CHAIN* chain, int /* free X509 */ #define wolfSSL_FreeX509(x509) wolfSSL_X509_free((x509)) WOLFSSL_ABI WOLFSSL_API void wolfSSL_X509_free(WOLFSSL_X509* x509); + /* get index cert in PEM */ WOLFSSL_API int wolfSSL_get_chain_cert_pem(WOLFSSL_X509_CHAIN* chain, int idx, unsigned char* buf, int inLen, int* outLen); @@ -3003,6 +3005,37 @@ WOLFSSL_API WOLFSSL_X509_CRL* wolfSSL_X509_CRL_dup(const WOLFSSL_X509_CRL* crl); WOLFSSL_API void wolfSSL_X509_CRL_free(WOLFSSL_X509_CRL *crl); #endif +#if defined(WOLFSSL_ACERT) +WOLFSSL_API void wolfSSL_X509_ACERT_init(WOLFSSL_X509_ACERT * x509, + void* heap); +WOLFSSL_API void wolfSSL_X509_ACERT_free(WOLFSSL_X509_ACERT* x509); +#ifndef NO_WOLFSSL_STUB +WOLFSSL_API int wolfSSL_X509_ACERT_sign(WOLFSSL_X509_ACERT * x509, + WOLFSSL_EVP_PKEY * pkey, + const WOLFSSL_EVP_MD * md); +#endif /* !NO_WOLFSSL_STUB */ +WOLFSSL_API int wolfSSL_X509_ACERT_verify(WOLFSSL_X509_ACERT* x509, + WOLFSSL_EVP_PKEY* pkey); +WOLFSSL_API int wolfSSL_X509_ACERT_print(WOLFSSL_BIO* bio, + WOLFSSL_X509_ACERT* x509_acert); +WOLFSSL_API int wolfSSL_X509_ACERT_get_attr_buf(const WOLFSSL_X509_ACERT* x509, + const byte ** rawAttr, + word32 * rawAttrLen); +WOLFSSL_API int wolfSSL_X509_ACERT_get_serial_number(WOLFSSL_X509_ACERT* x509, + unsigned char* in, + int * inOutSz); +WOLFSSL_API int wolfSSL_X509_ACERT_version(WOLFSSL_X509_ACERT* x509); +WOLFSSL_API long wolfSSL_X509_ACERT_get_version(const WOLFSSL_X509_ACERT *x); +WOLFSSL_API int wolfSSL_X509_ACERT_get_signature_nid(const WOLFSSL_X509_ACERT* x); +WOLFSSL_API int wolfSSL_X509_ACERT_get_signature(WOLFSSL_X509_ACERT* x509, + unsigned char* buf, + int* bufSz); +WOLFSSL_API WOLFSSL_X509_ACERT * wolfSSL_PEM_read_bio_X509_ACERT( + WOLFSSL_BIO *bp, WOLFSSL_X509_ACERT **x, wc_pem_password_cb *cb, void *u); +WOLFSSL_API WOLFSSL_X509_ACERT * wolfSSL_X509_ACERT_load_certificate_buffer( + const unsigned char* buf, int sz, int format); +#endif + WOLFSSL_API const WOLFSSL_ASN1_INTEGER* wolfSSL_X509_REVOKED_get0_serial_number(const WOLFSSL_X509_REVOKED *rev); diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 39ac82eaea..cc7073101c 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -2287,8 +2287,8 @@ WOLFSSL_LOCAL int SetShortInt(byte* input, word32* inOutIdx, word32 number, word32 maxIdx); WOLFSSL_LOCAL const char* GetSigName(int oid); -WOLFSSL_LOCAL int GetLength(const byte* input, word32* inOutIdx, int* len, - word32 maxIdx); +WOLFSSL_ASN_API int GetLength(const byte* input, word32* inOutIdx, int* len, + word32 maxIdx); WOLFSSL_LOCAL int GetLength_ex(const byte* input, word32* inOutIdx, int* len, word32 maxIdx, int check); WOLFSSL_LOCAL int GetASNHeader(const byte* input, byte tag, word32* inOutIdx, @@ -2332,8 +2332,8 @@ WOLFSSL_LOCAL int GetAlgoId(const byte* input, word32* inOutIdx, word32* oid, word32 oidType, word32 maxIdx); WOLFSSL_LOCAL int GetAlgoIdEx(const byte* input, word32* inOutIdx, word32* oid, word32 oidType, word32 maxIdx, byte *absentParams); -WOLFSSL_LOCAL int GetASNTag(const byte* input, word32* idx, byte* tag, - word32 inputSz); +WOLFSSL_ASN_API int GetASNTag(const byte* input, word32* idx, byte* tag, + word32 inputSz); WOLFSSL_LOCAL int GetASN_BitString(const byte* input, word32 idx, int length); WOLFSSL_LOCAL word32 SetASNLength(word32 length, byte* output); @@ -2707,9 +2707,56 @@ WOLFSSL_LOCAL int ParseCRL(RevokedCert* rcert, DecodedCRL* dcrl, const byte* buff, word32 sz, int verify, void* cm); WOLFSSL_LOCAL void FreeDecodedCRL(DecodedCRL* dcrl); - #endif /* HAVE_CRL */ +#if defined(WOLFSSL_ACERT) +/* Minimal structure for x509 attribute certificate (rfc 5755). + * + * The attributes field is not parsed, but is stored as raw buffer. + * + * */ +struct DecodedAcert { + word32 certBegin; /* Offset to start of acert. */ + word32 sigIndex; /* Offset to start of signature. */ + word32 sigLength; /* Signature length. */ + word32 signatureOID; /* Sum of algorithm object id. */ +#ifdef WC_RSA_PSS + word32 sigParamsIndex; /* start of signature parameters */ + word32 sigParamsLength; /* length of signature parameters */ +#endif + const byte * signature; /* Not owned, points into raw acert. */ + const byte * source; /* Byte buffer holding acert, NOT owned. */ + word32 srcIdx; /* Current offset into buffer. */ + word32 maxIdx; /* Max allowed offset. Set in init. */ + void * heap; /* For user memory overrides. */ + int version; /* attribute cert version. */ + byte serial[EXTERNAL_SERIAL_SIZE]; /* Raw serial number. */ + int serialSz; + const byte * beforeDate; /* Before and After dates. */ + int beforeDateLen; + const byte * afterDate; + int afterDateLen; + byte holderSerial[EXTERNAL_SERIAL_SIZE]; + int holderSerialSz; + DNS_entry * holderEntityName; /* Holder entityName from ACERT */ + DNS_entry * holderIssuerName; /* Holder issuerName from ACERT */ + DNS_entry * AttCertIssuerName; /* AttCertIssuer name from ACERT */ + const byte * rawAttr; /* Not owned, points into raw acert. */ + word32 rawAttrLen; + SignatureCtx sigCtx; +}; + +typedef struct DecodedAcert DecodedAcert; + +WOLFSSL_LOCAL void InitDecodedAcert(DecodedAcert* acert, + const byte* source, word32 inSz, + void* heap); +WOLFSSL_LOCAL void FreeDecodedAcert(DecodedAcert * acert); +WOLFSSL_LOCAL int ParseX509Acert(DecodedAcert* cert, int verify); +WOLFSSL_LOCAL int VerifyX509Acert(const byte* cert, word32 certSz, + const byte* pubKey, word32 pubKeySz, + int pubKeyOID, void * heap); +#endif /* WOLFSSL_ACERT */ #ifdef __cplusplus } /* extern "C" */ diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index ae77875774..83ef40eb46 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -148,6 +148,7 @@ enum CertType { CA_TYPE, ECC_PRIVATEKEY_TYPE, DSA_PRIVATEKEY_TYPE, + ACERT_TYPE, CERTREQ_TYPE, DSA_TYPE, ECC_TYPE, diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index f6dbdf6234..d272ae9749 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -3195,6 +3195,10 @@ extern void uITRON4_free(void *p) ; #error "Dual alg cert support requires the ASN.1 template feature." #endif +#if defined(WOLFSSL_ACERT) && !defined(WOLFSSL_ASN_TEMPLATE) + #error "Attribute Certificate support requires the ASN.1 template feature." +#endif + #if defined(OPENSSL_ALL) || defined(WOLFSSL_QT) #undef WOLFSSL_ASN_ALL #define WOLFSSL_ASN_ALL @@ -3290,6 +3294,11 @@ extern void uITRON4_free(void *p) ; /* Extended Key Usage */ #undef WOLFSSL_EKU_OID #define WOLFSSL_EKU_OID + + /* Attribute Certificate support */ + #if defined(WOLFSSL_ASN_TEMPLATE) && !defined(WOLFSSL_ACERT) + #define WOLFSSL_ACERT + #endif #endif #if defined(OPENSSL_ALL) || defined(WOLFSSL_MYSQL_COMPATIBLE) || \ diff --git a/wolfssl/wolfcrypt/types.h b/wolfssl/wolfcrypt/types.h index a5d8d31895..6784ee6a27 100644 --- a/wolfssl/wolfcrypt/types.h +++ b/wolfssl/wolfcrypt/types.h @@ -1097,6 +1097,7 @@ typedef struct w64wrapper { DYNAMIC_TYPE_DEBUG_TAG = 100, DYNAMIC_TYPE_LMS = 101, DYNAMIC_TYPE_BIO = 102, + DYNAMIC_TYPE_X509_ACERT = 103, DYNAMIC_TYPE_SNIFFER_SERVER = 1000, DYNAMIC_TYPE_SNIFFER_SESSION = 1001, DYNAMIC_TYPE_SNIFFER_PB = 1002,