Repository: randombit/botan
Pull request #2106 (2019-09-18 11:27:18)

Add X.509 Certificate Usage_Type::ENCRYPTION
This introduces a new entry to the `Usage_Type` enum in x509cert.h: `ENCRYPTION` that translates to "X509v3 Key Usage" containing either "Key Encipherment" or "Data Encipherment".

In our application we would like to check the key usage type as part of `x509_path_validate` rather than adding an extra manual check. However, the implementation of `X509_Certificate::allowed_usage()` states that it is based on [RFC 5280 4.2.1.12](https://tools.ietf.org/html/rfc5280#section-4.2.1.12) which does not explicitly list a fitting category.

What do you think?


Patch:
diff --git a/src/lib/x509/x509cert.cpp b/src/lib/x509/x509cert.cpp
index 96e66a5e0..130eaf5e2 100644
--- a/src/lib/x509/x509cert.cpp
+++ b/src/lib/x509/x509cert.cpp
@@ -535,6 +535,9 @@ bool X509_Certificate::allowed_usage(Usage_Type usage) const
 
       case Usage_Type::CERTIFICATE_AUTHORITY:
          return is_CA_cert();
+
+      case Usage_Type::ENCRYPTION:
+         return (allowed_usage(KEY_ENCIPHERMENT) || allowed_usage(DATA_ENCIPHERMENT));
       }
 
    return false;
diff --git a/src/lib/x509/x509cert.h b/src/lib/x509/x509cert.h
index 34be10e68..f65d76d23 100644
--- a/src/lib/x509/x509cert.h
+++ b/src/lib/x509/x509cert.h
@@ -28,7 +28,8 @@ enum class Usage_Type
    TLS_SERVER_AUTH,
    TLS_CLIENT_AUTH,
    CERTIFICATE_AUTHORITY,
-   OCSP_RESPONDER
+   OCSP_RESPONDER,
+   ENCRYPTION
    };
 
 struct X509_Certificate_Data;


Test patch:
diff --git a/src/tests/unit_x509.cpp b/src/tests/unit_x509.cpp
index 482b26531..d180e8ffc 100644
--- a/src/tests/unit_x509.cpp
+++ b/src/tests/unit_x509.cpp
@@ -955,6 +955,7 @@ Test::Result test_usage(const Botan::Private_Key& ca_key,
                         const std::string& hash_fn = "SHA-256")
    {
    using Botan::Key_Constraints;
+   using Botan::Usage_Type;
 
    Test::Result result("X509 Usage");
 
@@ -989,6 +990,8 @@ Test::Result test_usage(const Botan::Private_Key& ca_key,
    result.test_eq("key usage cRLSign not allowed",
                   user1_cert.allowed_usage(
                      Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE | Key_Constraints::CRL_SIGN)), false);
+   result.test_eq("encryption is not allowed",
+                  user1_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
 
    // cert only allows digitalSignature, so checking for only that should be ok
    result.confirm("key usage digitalSignature allowed", user1_cert.allowed_usage(Key_Constraints::DIGITAL_SIGNATURE));
@@ -1013,6 +1016,9 @@ Test::Result test_usage(const Botan::Private_Key& ca_key,
    result.confirm("key usage multiple cRLSign allowed", mult_usage_cert.allowed_usage(Key_Constraints::CRL_SIGN));
    result.confirm("key usage multiple digitalSignature and cRLSign allowed", mult_usage_cert.allowed_usage(
                      Key_Constraints(Key_Constraints::DIGITAL_SIGNATURE | Key_Constraints::CRL_SIGN)));
+   result.test_eq("encryption is not allowed",
+                  mult_usage_cert.allowed_usage(Usage_Type::ENCRYPTION), false);
+
 
    opts.constraints = Key_Constraints::NO_CONSTRAINTS;
 
@@ -1026,6 +1032,28 @@ Test::Result test_usage(const Botan::Private_Key& ca_key,
    // cert allows every usage
    result.confirm("key usage digitalSignature allowed", no_usage_cert.allowed_usage(Key_Constraints::DIGITAL_SIGNATURE));
    result.confirm("key usage cRLSign allowed", no_usage_cert.allowed_usage(Key_Constraints::CRL_SIGN));
+   result.confirm("key usage encryption allowed", no_usage_cert.allowed_usage(Usage_Type::ENCRYPTION));
+
+   if (sig_algo == "RSA")
+      {
+      // cert allows data encryption
+       opts.constraints = Key_Constraints(Key_Constraints::KEY_ENCIPHERMENT | Key_Constraints::DATA_ENCIPHERMENT);
+
+      const Botan::PKCS10_Request enc_req = Botan::X509::create_cert_req(
+               opts,
+               *user1_key,
+               hash_fn,
+               Test::rng());
+
+      const Botan::X509_Certificate enc_cert = ca.sign_request(
+               enc_req,
+               Test::rng(),
+               from_date(-1, 01, 01),
+               from_date(2, 01, 01));
+
+      result.confirm("cert allows encryption", enc_cert.allowed_usage(Usage_Type::ENCRYPTION));
+      result.confirm("cert does not allow TLS client auth", !enc_cert.allowed_usage(Usage_Type::TLS_CLIENT_AUTH));
+      }
 
    return result;
    }