[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1111966: bookworm-pu: package botan3/2.19.3+dfsg-1+deb12u1



Package: release.debian.org
Severity: normal
Tags: bookworm
X-Debbugs-Cc: botan3@packages.debian.org, gcs@debian.org
Control: affects -1 + src:botan3
User: release.debian.org@packages.debian.org
Usertags: pu

This update fixes various low severity security issues, debdiff
below.

Cheers,
        Moritz

diff -Nru botan-2.19.3+dfsg/debian/changelog botan-2.19.3+dfsg/debian/changelog
--- botan-2.19.3+dfsg/debian/changelog	2022-11-17 21:59:51.000000000 +0100
+++ botan-2.19.3+dfsg/debian/changelog	2025-08-24 14:57:06.000000000 +0200
@@ -1,3 +1,12 @@
+botan (2.19.3+dfsg-1+deb12u1) bookworm; urgency=medium
+
+  * CVE-2024-34702
+  * CVE-2024-34703
+  * CVE-2024-39312
+  * CVE-2024-50382/CVE-2024-50383 (Closes: #1086039)
+
+ -- Moritz Mühlenhoff <jmm@debian.org>  Sun, 24 Aug 2025 14:57:06 +0200
+
 botan (2.19.3+dfsg-1) unstable; urgency=high
 
   * New upstream release:
diff -Nru botan-2.19.3+dfsg/debian/patches/CVE-2024-34702.patch botan-2.19.3+dfsg/debian/patches/CVE-2024-34702.patch
--- botan-2.19.3+dfsg/debian/patches/CVE-2024-34702.patch	1970-01-01 01:00:00.000000000 +0100
+++ botan-2.19.3+dfsg/debian/patches/CVE-2024-34702.patch	2025-08-22 15:01:52.000000000 +0200
@@ -0,0 +1,719 @@
+From 68338f5912534c74469f7f4e6e22b37aa5159952 Mon Sep 17 00:00:00 2001
+From: Jack Lloyd <jack@randombit.net>
+Date: Sun, 7 Jul 2024 05:02:48 -0400
+Subject: [PATCH] Address various name constraint bugs
+
+--- botan-2.19.3+dfsg.orig/src/lib/x509/asn1_alt_name.cpp
++++ botan-2.19.3+dfsg/src/lib/x509/asn1_alt_name.cpp
+@@ -257,6 +257,10 @@ void AlternativeName::decode_from(BER_De
+             const uint32_t ip = load_be<uint32_t>(obj.bits(), 0);
+             add_attribute("IP", ipv4_to_string(ip));
+             }
++         else if(obj.length() != 16)
++            {
++            throw Decoding_Error("Invalid length for IP address SAN");
++            }
+          }
+ 
+       }
+--- botan-2.19.3+dfsg.orig/src/lib/x509/name_constraint.cpp
++++ botan-2.19.3+dfsg/src/lib/x509/name_constraint.cpp
+@@ -163,31 +163,48 @@ GeneralName::MatchResult GeneralName::ma
+ 
+ bool GeneralName::matches_dns(const std::string& nam) const
+    {
+-   if(nam.size() == name().size())
++   const std::string constraint = tolower_string(name());
++   const std::string issued = tolower_string(nam);
++
++   if(nam.size() == constraint.size())
+       {
+-      return tolower_string(nam) == tolower_string(name());
++      return issued == constraint;
+       }
+-   else if(name().size() > nam.size())
++   else if(constraint.size() > nam.size())
+       {
+       // The constraint is longer than the issued name: not possibly a match
+       return false;
+       }
+-   else // name.size() < nam.size()
++   else
+       {
+-      // constr is suffix of nam
+-      const std::string constr = name().front() == '.' ? name() : "." + name();
+-      const std::string substr = nam.substr(nam.size() - constr.size(), constr.size());
+-      return tolower_string(constr) == tolower_string(substr);
++      if(constraint.empty()) {
++         return true;
++      }
++
++      std::string substr = issued.substr(nam.size() - constraint.size(), constraint.size());
++
++      if(constraint.front() == '.') {
++         return substr == constraint;
++      } else if(substr[0] == '.') {
++         return substr.substr(1) == constraint;
++      } else {
++         return substr == constraint && issued[issued.size() - constraint.size() - 1] == '.';
+       }
+    }
++}
+ 
+ bool GeneralName::matches_dn(const std::string& nam) const
+    {
+    std::stringstream ss(nam);
+-   std::stringstream tt(name());
+-   X509_DN nam_dn, my_dn;
+-
++   X509_DN nam_dn;
+    ss >> nam_dn;
++   return matches_dn_obj(nam_dn);
++   }
++
++bool GeneralName::matches_dn_obj(const X509_DN& nam_dn) const
++   {
++   std::stringstream tt(name());
++   X509_DN my_dn;
+    tt >> my_dn;
+ 
+    auto attr = nam_dn.get_attributes();
+@@ -270,4 +287,278 @@ std::ostream& operator<<(std::ostream& o
+    os << gs.minimum() << "," << gs.maximum() << "," << gs.base();
+    return os;
+    }
++
++NameConstraints::NameConstraints(std::vector<GeneralSubtree>&& permitted_subtrees,
++                                 std::vector<GeneralSubtree>&& excluded_subtrees) :
++   m_permitted_subtrees(permitted_subtrees), m_excluded_subtrees(excluded_subtrees)
++   {
++   for(const auto& c : m_permitted_subtrees)
++      {
++      m_permitted_name_types.insert(c.base().type());
++      }
++   for(const auto& c : m_excluded_subtrees)
++      {
++      m_excluded_name_types.insert(c.base().type());
++      }
++   }
++
++namespace {
++
++bool looks_like_ipv4(const std::string& s)
++   {
++   try
++     {
++     // ignores return value
++     string_to_ipv4(s);
++     return true;
++     }
++   catch(...)
++      {
++      return false;
++      }
++   }
++
++}
++
++bool NameConstraints::is_permitted(const X509_Certificate& cert, bool reject_unknown) const {
++   if(permitted().empty()) {
++      return true;
++   }
++
++   const auto& alt_name = cert.subject_alt_name();
++
++   if(reject_unknown) {
++      if(m_permitted_name_types.find("URI") != m_permitted_name_types.end() && !alt_name.get_attribute("URI").empty()) {
++         return false;
++      }
++      if(m_permitted_name_types.find("RFC822") != m_permitted_name_types.end() && !alt_name.get_attribute("RFC822").empty()) {
++         return false;
++      }
++   }
++
++   auto is_permitted_dn = [&](const X509_DN& dn) {
++      // If no restrictions, then immediate accept
++      if(m_permitted_name_types.find("DN") == m_permitted_name_types.end()) {
++         return true;
++      }
++
++      if(dn.empty()) {
++         return true;
++      }
++
++      for(const auto& c : m_permitted_subtrees) {
++         if(c.base().type() == "DN" && c.base().matches_dn_obj(dn)) {
++            return true;
++         }
++      }
++
++      // There is at least one permitted name and we didn't match
++      return false;
++   };
++
++   auto is_permitted_dns_name = [&](const std::string& name) {
++      if(name.empty() || name[0] == '.') {
++         return false;
++      }
++
++      // If no restrictions, then immediate accept
++      if(m_permitted_name_types.find("DNS") == m_permitted_name_types.end()) {
++         return true;
++      }
++
++      for(const auto& c : m_permitted_subtrees) {
++         if(c.base().type() == "DNS" && c.base().matches_dns(name)) {
++            return true;
++         }
++      }
++
++      // There is at least one permitted name and we didn't match
++      return false;
++   };
++
++   auto is_permitted_ipv4 = [&](const std::string& ipv4) {
++      // If no restrictions, then immediate accept
++      if(m_permitted_name_types.find("IP") == m_permitted_name_types.end()) {
++         return true;
++      }
++
++      for(const auto& c : m_permitted_subtrees) {
++         if(c.base().type() == "IP" && c.base().matches_ip(ipv4)) {
++            return true;
++         }
++      }
++
++      // There is at least one permitted name and we didn't match
++      return false;
++   };
++
++   if(!is_permitted_dn(cert.subject_dn())) {
++      return false;
++   }
++
++   if(!is_permitted_dn(alt_name.dn()))
++      {
++      return false;
++      }
++
++   for(const auto& alt_dns : alt_name.get_attribute("DNS")) {
++      if(!is_permitted_dns_name(alt_dns)) {
++         return false;
++      }
++   }
++
++   for(const auto& alt_ipv4 : alt_name.get_attribute("IP")) {
++      if(!is_permitted_ipv4(alt_ipv4)) {
++         return false;
++      }
++   }
++
++   if(!alt_name.has_items())
++      {
++      for(const auto& cn : cert.subject_info("Name"))
++         {
++         if(cn.find(".") != std::string::npos)
++            {
++            if(looks_like_ipv4(cn))
++               {
++               if(!is_permitted_ipv4(cn))
++                  {
++                  return false;
++                  }
++               }
++            else
++               {
++               if(!is_permitted_dns_name(cn))
++                  {
++                  return false;
++                  }
++               }
++            }
++         }
++      }
++
++   // We didn't encounter a name that doesn't have a matching constraint
++   return true;
+ }
++
++bool NameConstraints::is_excluded(const X509_Certificate& cert, bool reject_unknown) const {
++   if(excluded().empty()) {
++      return false;
++   }
++
++   const auto& alt_name = cert.subject_alt_name();
++
++   if(reject_unknown) {
++      if(m_excluded_name_types.find("URI") != m_excluded_name_types.end() && !alt_name.get_attribute("URI").empty()) {
++         return false;
++      }
++      if(m_excluded_name_types.find("RFC822") != m_excluded_name_types.end() && !alt_name.get_attribute("RFC822").empty()) {
++         return false;
++      }
++   }
++
++   auto is_excluded_dn = [&](const X509_DN& dn) {
++      // If no restrictions, then immediate accept
++      if(m_excluded_name_types.find("DN") == m_excluded_name_types.end()) {
++         return false;
++      }
++
++      if(dn.empty()) {
++         return false;
++      }
++
++      for(const auto& c : m_excluded_subtrees) {
++         if(c.base().type() == "DN" && c.base().matches_dn_obj(dn)) {
++            return true;
++         }
++      }
++
++      // There is at least one excluded name and we didn't match
++      return false;
++   };
++
++   auto is_excluded_dns_name = [&](const std::string& name) {
++      if(name.empty() || name[0] == '.') {
++         return true;
++      }
++
++      // If no restrictions, then immediate accept
++      if(m_excluded_name_types.find("DNS") == m_excluded_name_types.end()) {
++         return false;
++      }
++
++      for(const auto& c : m_excluded_subtrees) {
++         if(c.base().type() == "DNS" && c.base().matches_dns(name)) {
++            return true;
++         }
++      }
++
++      // There is at least one excluded name and we didn't match
++      return false;
++   };
++
++   auto is_excluded_ipv4 = [&](const std::string& ipv4) {
++      // If no restrictions, then immediate accept
++      if(m_excluded_name_types.find("IP") == m_excluded_name_types.end()) {
++         return false;
++      }
++
++      for(const auto& c : m_excluded_subtrees) {
++         if(c.base().type() == "IP" && c.base().matches_ip(ipv4)) {
++            return true;
++         }
++      }
++
++      // There is at least one excluded name and we didn't match
++      return false;
++   };
++
++   if(is_excluded_dn(cert.subject_dn())) {
++      return true;
++   }
++
++   if(is_excluded_dn(alt_name.dn())) {
++      return true;
++   }
++
++   for(const auto& alt_dns : alt_name.get_attribute("DNS")) {
++      if(is_excluded_dns_name(alt_dns)) {
++         return true;
++      }
++   }
++
++   for(const auto& alt_ipv4 : alt_name.get_attribute("IP")) {
++      if(is_excluded_ipv4(alt_ipv4)) {
++         return true;
++      }
++   }
++
++   if(!alt_name.has_items())
++      {
++      for(const auto& cn : cert.subject_info("Name"))
++         {
++         if(cn.find(".") != std::string::npos)
++            {
++            if(looks_like_ipv4(cn))
++               {
++               if(is_excluded_ipv4(cn))
++                  {
++                  return true;
++                  }
++               }
++            else
++               {
++               if(is_excluded_dns_name(cn))
++                  {
++                  return true;
++                  }
++               }
++            }
++         }
++      }
++
++   // We didn't encounter a name that matched any prohibited name
++   return false;
++}
++
++}  // namespace Botan
+--- botan-2.19.3+dfsg.orig/src/lib/x509/pkix_types.h
++++ botan-2.19.3+dfsg/src/lib/x509/pkix_types.h
+@@ -191,6 +191,9 @@ class BOTAN_PUBLIC_API(2,0) Attribute fi
+ * Handles parsing GeneralName types in their BER and canonical string
+ * encoding. Allows matching GeneralNames against each other using
+ * the rules laid out in the RFC 5280, sec. 4.2.1.10 (Name Contraints).
++*
++* This entire class is deprecated and will be removed in a future
++* major release
+ */
+ class BOTAN_PUBLIC_API(2,0) GeneralName final : public ASN1_Object
+    {
+@@ -213,6 +216,7 @@ class BOTAN_PUBLIC_API(2,0) GeneralName
+       * Creates a new GeneralName for its string format.
+       * @param str type and name, colon-separated, e.g., "DNS:google.com"
+       */
++      BOTAN_DEPRECATED("Deprecated no replacement")
+       GeneralName(const std::string& str);
+ 
+       void encode_into(DER_Encoder&) const override;
+@@ -234,15 +238,17 @@ class BOTAN_PUBLIC_API(2,0) GeneralName
+       * @param cert certificate to be matched
+       * @return the match result
+       */
++      BOTAN_DEPRECATED("Deprecated no replacement")
+       MatchResult matches(const X509_Certificate& cert) const;
+ 
+-   private:
+-      std::string m_type;
+-      std::string m_name;
+-
+       bool matches_dns(const std::string&) const;
+       bool matches_dn(const std::string&) const;
++      bool matches_dn_obj(const X509_DN& dn) const;
+       bool matches_ip(const std::string&) const;
++
++   private:
++      std::string m_type;
++      std::string m_name;
+    };
+ 
+ std::ostream& operator<<(std::ostream& os, const GeneralName& gn);
+@@ -253,6 +259,9 @@ std::ostream& operator<<(std::ostream& o
+ * The Name Constraint extension adds a minimum and maximum path
+ * length to a GeneralName to form a constraint. The length limits
+ * are currently unused.
++*
++* This entire class is deprecated and will be removed in a future
++* major release
+ */
+ class BOTAN_PUBLIC_API(2,0) GeneralSubtree final : public ASN1_Object
+    {
+@@ -260,6 +269,7 @@ class BOTAN_PUBLIC_API(2,0) GeneralSubtr
+       /**
+       * Creates an empty name constraint.
+       */
++      BOTAN_DEPRECATED("Deprecated no replacement")
+       GeneralSubtree() : m_base(), m_minimum(0), m_maximum(std::numeric_limits<std::size_t>::max())
+       {}
+ 
+@@ -269,6 +279,7 @@ class BOTAN_PUBLIC_API(2,0) GeneralSubtr
+       * @param min minimum path length
+       * @param max maximum path length
+       */
++      BOTAN_DEPRECATED("Deprecated no replacement")
+       GeneralSubtree(const GeneralName& base, size_t min, size_t max)
+       : m_base(base), m_minimum(min), m_maximum(max)
+       {}
+@@ -277,6 +288,7 @@ class BOTAN_PUBLIC_API(2,0) GeneralSubtr
+       * Creates a new name constraint for its string format.
+       * @param str name constraint
+       */
++      BOTAN_DEPRECATED("Deprecated no replacement")
+       GeneralSubtree(const std::string& str);
+ 
+       void encode_into(DER_Encoder&) const override;
+@@ -325,9 +337,7 @@ class BOTAN_PUBLIC_API(2,0) NameConstrai
+       * @param excluded_subtrees names for which the certificate is not permitted
+       */
+       NameConstraints(std::vector<GeneralSubtree>&& permitted_subtrees,
+-                    std::vector<GeneralSubtree>&& excluded_subtrees)
+-      : m_permitted_subtrees(permitted_subtrees), m_excluded_subtrees(excluded_subtrees)
+-      {}
++                      std::vector<GeneralSubtree>&& excluded_subtrees);
+ 
+       /**
+       * @return permitted names
+@@ -339,9 +349,22 @@ class BOTAN_PUBLIC_API(2,0) NameConstrai
+       */
+       const std::vector<GeneralSubtree>& excluded() const { return m_excluded_subtrees; }
+ 
++      /**
++      * Return true if all of the names in the certificate are permitted
++      */
++      bool is_permitted(const X509_Certificate& cert, bool reject_unknown) const;
++
++      /**
++      * Return true if any of the names in the certificate are excluded
++      */
++      bool is_excluded(const X509_Certificate& cert, bool reject_unknown) const;
++
+    private:
+       std::vector<GeneralSubtree> m_permitted_subtrees;
+       std::vector<GeneralSubtree> m_excluded_subtrees;
++
++      std::set<std::string> m_permitted_name_types;
++      std::set<std::string> m_excluded_name_types;
+    };
+ 
+ /**
+--- botan-2.19.3+dfsg.orig/src/lib/x509/x509_ext.cpp
++++ botan-2.19.3+dfsg/src/lib/x509/x509_ext.cpp
+@@ -601,27 +601,27 @@ void Name_Constraints::decode_inner(cons
+    {
+    std::vector<GeneralSubtree> permit, exclude;
+    BER_Decoder ber(in);
+-   BER_Decoder ext = ber.start_cons(SEQUENCE);
+-   BER_Object per = ext.get_next_object();
++   BER_Decoder inner = ber.start_cons(SEQUENCE);
++   BER_Object per = inner.get_next_object();
+ 
+-   ext.push_back(per);
++   inner.push_back(per);
+    if(per.is_a(0, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+       {
+-      ext.decode_list(permit,ASN1_Tag(0),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
++      inner.decode_list(permit,ASN1_Tag(0),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+       if(permit.empty())
+          throw Encoding_Error("Empty Name Contraint list");
+       }
+ 
+-   BER_Object exc = ext.get_next_object();
+-   ext.push_back(exc);
+-   if(per.is_a(1, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
++   BER_Object exc = inner.get_next_object();
++   inner.push_back(exc);
++   if(exc.is_a(1, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+       {
+-      ext.decode_list(exclude,ASN1_Tag(1),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
++      inner.decode_list(exclude,ASN1_Tag(1),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+       if(exclude.empty())
+          throw Encoding_Error("Empty Name Contraint list");
+       }
+ 
+-   ext.end_cons();
++   inner.end_cons();
+ 
+    if(permit.empty() && exclude.empty())
+       throw Encoding_Error("Empty Name Contraint extension");
+@@ -650,11 +650,17 @@ void Name_Constraints::contents_to(Data_
+       }
+    }
+ 
+-void Name_Constraints::validate(const X509_Certificate& subject, const X509_Certificate& issuer,
++void Name_Constraints::validate(const X509_Certificate& subject, const X509_Certificate& /*issuer*/,
+       const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+       std::vector<std::set<Certificate_Status_Code>>& cert_status,
+       size_t pos)
+    {
++   // This is much smaller limit than in Botan3 because here name constraint checks
++   // are much more expensive due to optimizations which would be difficult to
++   // backport here.
++   const size_t MAX_NC_COMPARES = (1 << 12);
++   const size_t total_constraints = m_name_constraints.permitted().size() + m_name_constraints.excluded().size();
++
+    if(!m_name_constraints.permitted().empty() || !m_name_constraints.excluded().empty())
+       {
+       if(!subject.is_CA_cert())
+@@ -663,54 +669,34 @@ void Name_Constraints::validate(const X5
+          }
+ 
+       const bool issuer_name_constraint_critical =
+-         issuer.is_critical("X509v3.NameConstraints");
++         subject.is_critical("X509v3.NameConstraints");
+ 
+       // Check that all subordinate certs pass the name constraint
+       for(size_t j = 0; j < pos; ++j)
+          {
+-         bool permitted = m_name_constraints.permitted().empty();
+-         bool failed = false;
++         const auto& cert = cert_path.at(j);
++
++         const size_t total_names =
++            cert->subject_dn().dn_info().size() +
++            cert->subject_alt_name().get_attributes().size();
+ 
+-         for(auto c: m_name_constraints.permitted())
+-            {
+-            switch(c.base().matches(*cert_path.at(j)))
+-               {
+-               case GeneralName::MatchResult::NotFound:
+-               case GeneralName::MatchResult::All:
+-                  permitted = true;
+-                  break;
+-               case GeneralName::MatchResult::UnknownType:
+-                  failed = issuer_name_constraint_critical;
+-                  permitted = true;
+-                  break;
+-               default:
+-                  break;
+-               }
+-            }
+-
+-         for(auto c: m_name_constraints.excluded())
+-            {
+-            switch(c.base().matches(*cert_path.at(j)))
+-               {
+-               case GeneralName::MatchResult::All:
+-               case GeneralName::MatchResult::Some:
+-                  failed = true;
+-                  break;
+-               case GeneralName::MatchResult::UnknownType:
+-                  failed = issuer_name_constraint_critical;
+-                  break;
+-               default:
+-                  break;
+-               }
+-            }
++         if(total_names * total_constraints >= MAX_NC_COMPARES) {
++            cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
++            continue;
++         }
++
++         if(!m_name_constraints.is_permitted(*cert, issuer_name_constraint_critical)) {
++            cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
++            continue;
++         }
+ 
+-         if(failed || !permitted)
+-            {
++         if(m_name_constraints.is_excluded(*cert, issuer_name_constraint_critical)) {
+             cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
+-            }
++            continue;
+          }
+       }
+    }
++}
+ 
+ namespace {
+ 
+--- botan-2.19.3+dfsg.orig/src/lib/x509/x509cert.cpp
++++ botan-2.19.3+dfsg/src/lib/x509/x509cert.cpp
+@@ -17,6 +17,7 @@
+ #include <botan/oids.h>
+ #include <botan/hash.h>
+ #include <botan/hex.h>
++#include <botan/internal/stl_util.h>
+ #include <algorithm>
+ #include <sstream>
+ 
+@@ -788,16 +789,35 @@ bool X509_Certificate::matches_dns_name(
+    if(name.empty())
+       return false;
+ 
+-   std::vector<std::string> issued_names = subject_info("DNS");
++   bool is_ipv4 = false;
+ 
+-   // Fall back to CN only if no DNS names are set (RFC 6125 sec 6.4.4)
+-   if(issued_names.empty())
++   try {
++      string_to_ipv4(name);
++      is_ipv4 = true;
++      }
++   catch(...) {}
++
++   std::vector<std::string> issued_names;
++
++   if(subject_alt_name().has_items()) {
++      issued_names = subject_alt_name().get_attribute(is_ipv4 ? "IP" : "DNS");
++   } else if(is_ipv4 == false) {
++      // Use CN only if no SAN is included
+       issued_names = subject_info("Name");
++   }
+ 
+    for(size_t i = 0; i != issued_names.size(); ++i)
+       {
+-      if(host_wildcard_match(issued_names[i], name))
+-         return true;
++      if(is_ipv4)
++         {
++         if(issued_names[i] == name)
++            return true;
++         }
++      else
++         {
++         if(host_wildcard_match(issued_names[i], name))
++            return true;
++         }
+       }
+ 
+    return false;
+--- botan-2.19.3+dfsg.orig/src/python/botan2.py
++++ botan-2.19.3+dfsg/src/python/botan2.py
+@@ -1285,6 +1285,7 @@ def _load_buf_or_file(filename, buf, fil
+ #
+ class X509Cert(object): # pylint: disable=invalid-name
+     def __init__(self, filename=None, buf=None):
++        self.__obj = c_void_p(0)
+         self.__obj = _load_buf_or_file(filename, buf, _DLL.botan_x509_cert_load_file, _DLL.botan_x509_cert_load)
+ 
+     def __del__(self):
+@@ -1464,6 +1465,7 @@ class X509Cert(object): # pylint: disabl
+ #
+ class X509CRL(object):
+     def __init__(self, filename=None, buf=None):
++        self.__obj = c_void_p(0)
+         self.__obj = _load_buf_or_file(filename, buf, _DLL.botan_x509_crl_load_file, _DLL.botan_x509_crl_load)
+ 
+     def __del__(self):
+--- botan-2.19.3+dfsg.orig/src/scripts/test_python.py
++++ botan-2.19.3+dfsg/src/scripts/test_python.py
+@@ -474,9 +474,6 @@ ofvkP1EDmpx50fHLawIDAQAB
+         self.assertEqual(cert.issuer_dn('Organizational Unit', 0), 'bsi')
+         self.assertEqual(cert.issuer_dn('Country', 0), 'DE')
+ 
+-        self.assertTrue(cert.hostname_match('csca-germany'))
+-        self.assertFalse(cert.hostname_match('csca-slovakia'))
+-
+         self.assertEqual(cert.not_before(), 1184858838)
+         self.assertEqual(cert.not_after(), 1831907880)
+ 
+--- botan-2.19.3+dfsg.orig/src/tests/test_name_constraint.cpp
++++ botan-2.19.3+dfsg/src/tests/test_name_constraint.cpp
+@@ -29,17 +29,17 @@ class Name_Constraint_Tests final : publ
+             std::make_tuple(
+                "Root_Email_Name_Constraint.crt",
+                "Invalid_Email_Name_Constraint.crt",
+-               "Invalid Email Name Constraint",
++               "",
+                "Certificate does not pass name constraint"),
+             std::make_tuple(
+                "Root_DN_Name_Constraint.crt",
+                "Invalid_DN_Name_Constraint.crt",
+-               "Invalid DN Name Constraint",
++               "",
+                "Certificate does not pass name constraint"),
+             std::make_tuple(
+                "Root_DN_Name_Constraint.crt",
+                "Valid_DN_Name_Constraint.crt",
+-               "Valid DN Name Constraint",
++               "",
+                "Verified"),
+             std::make_tuple(
+                "Root_DNS_Name_Constraint.crt",
+@@ -49,12 +49,12 @@ class Name_Constraint_Tests final : publ
+             std::make_tuple(
+                "Root_IP_Name_Constraint.crt",
+                "Valid_IP_Name_Constraint.crt",
+-               "Valid IP Name Constraint",
++               "",
+                "Verified"),
+             std::make_tuple(
+                "Root_IP_Name_Constraint.crt",
+                "Invalid_IP_Name_Constraint.crt",
+-               "Invalid IP Name Constraint",
++               "",
+                "Certificate does not pass name constraint"),
+             };
+          std::vector<Test::Result> results;
diff -Nru botan-2.19.3+dfsg/debian/patches/CVE-2024-34703.patch botan-2.19.3+dfsg/debian/patches/CVE-2024-34703.patch
--- botan-2.19.3+dfsg/debian/patches/CVE-2024-34703.patch	1970-01-01 01:00:00.000000000 +0100
+++ botan-2.19.3+dfsg/debian/patches/CVE-2024-34703.patch	2025-08-22 15:02:33.000000000 +0200
@@ -0,0 +1,22 @@
+From 94e9154c143aa5264da6254a6a1be5bc66ee2b5a Mon Sep 17 00:00:00 2001
+From: Jack Lloyd <jack@randombit.net>
+Date: Tue, 20 Feb 2024 06:32:57 -0500
+Subject: [PATCH] When decoding an arbitrary elliptic curve, set an upper bound
+ on length
+
+--- botan-2.19.3+dfsg.orig/src/lib/pubkey/ec_group/ec_group.cpp
++++ botan-2.19.3+dfsg/src/lib/pubkey/ec_group/ec_group.cpp
+@@ -334,8 +334,11 @@ std::shared_ptr<EC_Group_Data> EC_Group:
+          .end_cons()
+          .verify_end();
+ 
+-      if(p.bits() < 64 || p.is_negative() || !is_bailie_psw_probable_prime(p))
+-         throw Decoding_Error("Invalid ECC p parameter");
++      if(p.bits() < 112 || p.bits() > 1024)
++         throw Decoding_Error("ECC p parameter is invalid size");
++
++      if(p.is_negative() || !is_bailie_psw_probable_prime(p))
++         throw Decoding_Error("ECC p parameter is not a prime");
+ 
+       if(a.is_negative() || a >= p)
+          throw Decoding_Error("Invalid ECC a parameter");
diff -Nru botan-2.19.3+dfsg/debian/patches/CVE-2024-39312.patch botan-2.19.3+dfsg/debian/patches/CVE-2024-39312.patch
--- botan-2.19.3+dfsg/debian/patches/CVE-2024-39312.patch	1970-01-01 01:00:00.000000000 +0100
+++ botan-2.19.3+dfsg/debian/patches/CVE-2024-39312.patch	2025-08-22 15:03:22.000000000 +0200
@@ -0,0 +1,118 @@
+From cbbcc934043a0b1fa573a0022d002fd93f40214d Mon Sep 17 00:00:00 2001
+From: Jack Lloyd <jack@randombit.net>
+Date: Sat, 11 May 2024 14:32:36 -0400
+Subject: [PATCH] During X.509 verification, first check the signatures
+
+--- botan-2.19.3+dfsg.orig/src/lib/x509/x509path.cpp
++++ botan-2.19.3+dfsg/src/lib/x509/x509path.cpp
+@@ -51,6 +51,70 @@ PKIX::check_chain(const std::vector<std:
+    if(!cert_path[0]->allowed_usage(usage))
+       cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
+ 
++   for(size_t i = 0; i != cert_path.size(); ++i)
++      {
++      std::set<Certificate_Status_Code>& status = cert_status.at(i);
++
++      const bool at_self_signed_root = (i == cert_path.size() - 1);
++
++      const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
++
++      const std::shared_ptr<const X509_Certificate>& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
++
++      std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
++
++      // Check the signature algorithm is known
++      if(OIDS::oid2str_or_empty(subject->signature_algorithm().get_oid()).empty())
++         {
++         status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
++         }
++      else
++         {
++         // only perform the following checks if the signature algorithm is known
++         if(!issuer_key)
++            {
++            status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
++            }
++         else
++            {
++            const Certificate_Status_Code sig_status = subject->verify_signature(*issuer_key);
++
++            if(sig_status != Certificate_Status_Code::VERIFIED)
++               status.insert(sig_status);
++
++            if(issuer_key->estimated_strength() < min_signature_algo_strength)
++               status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
++            }
++
++         // Ignore untrusted hashes on self-signed roots
++         if(trusted_hashes.size() > 0 && !at_self_signed_root)
++            {
++            if(trusted_hashes.count(subject->hash_used_for_signature()) == 0)
++               status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
++            }
++         }
++      }
++
++
++   // If any of the signatures were invalid, return immediately; we know the
++   // chain is invalid and signature failure is always considered the most
++   // critical result. This does mean other problems in the certificate (eg
++   // expired) will not be reported, but we'd have to assume any such data is
++   // anyway arbitrary considering we couldn't verify the signature chain
++
++   for(size_t i = 0; i != cert_path.size(); ++i)
++      {
++      for(auto status : cert_status.at(i))
++         {
++         // This ignores errors relating to the key or hash being weak since
++         // these are somewhat advisory
++         if(static_cast<uint32_t>(status) >= 5000)
++            {
++            return cert_status;
++            }
++         }
++      }
++
+    if(cert_path[0]->is_CA_cert() == false &&
+       cert_path[0]->has_constraints(KEY_CERT_SIGN))
+       {
+@@ -114,39 +178,6 @@ PKIX::check_chain(const std::vector<std:
+       if(!issuer->is_CA_cert() && !self_signed_ee_cert)
+          status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
+ 
+-      std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
+-
+-      // Check the signature algorithm is known
+-      if(OIDS::oid2str_or_empty(subject->signature_algorithm().get_oid()).empty())
+-         {
+-         status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
+-         }
+-      else
+-         {
+-         // only perform the following checks if the signature algorithm is known
+-         if(!issuer_key)
+-            {
+-            status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
+-            }
+-         else
+-            {
+-            const Certificate_Status_Code sig_status = subject->verify_signature(*issuer_key);
+-
+-            if(sig_status != Certificate_Status_Code::VERIFIED)
+-               status.insert(sig_status);
+-
+-            if(issuer_key->estimated_strength() < min_signature_algo_strength)
+-               status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
+-            }
+-
+-         // Ignore untrusted hashes on self-signed roots
+-         if(trusted_hashes.size() > 0 && !at_self_signed_root)
+-            {
+-            if(trusted_hashes.count(subject->hash_used_for_signature()) == 0)
+-               status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
+-            }
+-         }
+-
+       // Check cert extensions
+ 
+       if(subject->x509_version() == 1)
diff -Nru botan-2.19.3+dfsg/debian/patches/CVE-2024-50382-CVE-2024-50383.patch botan-2.19.3+dfsg/debian/patches/CVE-2024-50382-CVE-2024-50383.patch
--- botan-2.19.3+dfsg/debian/patches/CVE-2024-50382-CVE-2024-50383.patch	1970-01-01 01:00:00.000000000 +0100
+++ botan-2.19.3+dfsg/debian/patches/CVE-2024-50382-CVE-2024-50383.patch	2025-08-22 15:04:03.000000000 +0200
@@ -0,0 +1,45 @@
+From 53b0cfde580e86b03d0d27a488b6c134f662e957 Mon Sep 17 00:00:00 2001
+From: Jack Lloyd <jack@randombit.net>
+Date: Sat, 19 Oct 2024 07:43:18 -0400
+Subject: [PATCH] Add more value barriers to avoid compiler induced side
+ channels
+
+--- botan-2.19.3+dfsg.orig/src/lib/utils/donna128.h
++++ botan-2.19.3+dfsg/src/lib/utils/donna128.h
+@@ -8,6 +8,7 @@
+ #ifndef BOTAN_CURVE25519_DONNA128_H_
+ #define BOTAN_CURVE25519_DONNA128_H_
+ 
++#include <botan/internal/ct_utils.h>
+ #include <botan/mul128.h>
+ 
+ namespace Botan {
+@@ -61,7 +62,7 @@ class donna128 final
+          l += x.l;
+          h += x.h;
+ 
+-         const uint64_t carry = (l < x.l);
++         const uint64_t carry = CT::Mask<uint64_t>::is_lt(l, x.l).if_set_return(1);
+          h += carry;
+          return *this;
+          }
+@@ -69,7 +70,7 @@ class donna128 final
+       donna128& operator+=(uint64_t x)
+          {
+          l += x;
+-         const uint64_t carry = (l < x);
++         const uint64_t carry = CT::Mask<uint64_t>::is_lt(l, x).if_set_return(1);
+          h += carry;
+          return *this;
+          }
+--- botan-2.19.3+dfsg.orig/src/lib/utils/ghash/ghash.cpp
++++ botan-2.19.3+dfsg/src/lib/utils/ghash/ghash.cpp
+@@ -139,7 +139,7 @@ void GHASH::key_schedule(const uint8_t k
+          m_HM[4*j+2*i+1] = H1;
+ 
+          // GCM's bit ops are reversed so we carry out of the bottom
+-         const uint64_t carry = R * (H1 & 1);
++         const uint64_t carry = CT::Mask<uint64_t>::expand(H1 & 1).if_set_return(R);
+          H1 = (H1 >> 1) | (H0 << 63);
+          H0 = (H0 >> 1) ^ carry;
+          }
diff -Nru botan-2.19.3+dfsg/debian/patches/series botan-2.19.3+dfsg/debian/patches/series
--- botan-2.19.3+dfsg/debian/patches/series	2022-06-18 11:56:15.000000000 +0200
+++ botan-2.19.3+dfsg/debian/patches/series	2025-08-22 15:03:52.000000000 +0200
@@ -1,2 +1,6 @@
 readdir_hurd.patch
 use_python3.patch
+CVE-2024-34702.patch
+CVE-2024-34703.patch
+CVE-2024-39312.patch
+CVE-2024-50382-CVE-2024-50383.patch

Reply to: