botan: patch CVE-2026-32877

Details: https://nvd.nist.gov/vuln/detail/CVE-2026-32877

Backport the patch that was identified by Debian[1].
The included test passed successfully (along with the other tests).

[1]: https://security-tracker.debian.org/tracker/CVE-2026-32877

Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
Gyorgy Sarvari
2026-04-06 20:32:53 +02:00
committed by Anuj Mittal
parent ab0866131d
commit c4b5bca1e8
2 changed files with 161 additions and 1 deletions
@@ -0,0 +1,158 @@
From a0048a6b97a349a3cb4a5f955d350ab2921719cc Mon Sep 17 00:00:00 2001
From: Jack Lloyd <jack@randombit.net>
Date: Sun, 15 Mar 2026 11:39:50 -0400
Subject: [PATCH] In SM2 verify the C3 field is of the required length
Previously the decryption step assumed that C3 was equal in length to the MAC
output. If C3 is shorter than expected, up to 31 bytes of arbitrary heap data
would be compared with the computed MAC. This heap over-read would potentially
result in denial of service.
CVE: CVE-2026-32877
Upstream-Status: Backport [https://github.com/randombit/botan/commit/f3c31f96f58f1d1d482032d8f4286dc9ebbc6712]
Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
---
src/lib/pubkey/sm2/sm2_enc.cpp | 7 +++-
src/tests/data/pubkey/sm2_invalid.vec | 60 +++++++++++++++++++++++++++
src/tests/test_sm2.cpp | 31 ++++++++++++++
3 files changed, 97 insertions(+), 1 deletion(-)
create mode 100644 src/tests/data/pubkey/sm2_invalid.vec
diff --git a/src/lib/pubkey/sm2/sm2_enc.cpp b/src/lib/pubkey/sm2/sm2_enc.cpp
index 7a1b990..1141089 100644
--- a/src/lib/pubkey/sm2/sm2_enc.cpp
+++ b/src/lib/pubkey/sm2/sm2_enc.cpp
@@ -133,6 +133,11 @@ class SM2_Decryption_Operation final : public PK_Ops::Decryption {
.end_cons()
.verify_end();
+ // Wrong length so certainly invalid, reject immediately
+ if(C3.size() != m_hash->output_length()) {
+ return secure_vector<uint8_t>();
+ }
+
std::vector<uint8_t> recode_ctext;
DER_Encoder(recode_ctext)
.start_sequence()
@@ -170,7 +175,7 @@ class SM2_Decryption_Operation final : public PK_Ops::Decryption {
m_hash->update(y2_bytes);
const auto u = m_hash->final();
- if(!CT::is_equal(u.data(), C3.data(), m_hash->output_length()).as_bool()) {
+ if(!CT::is_equal<uint8_t>(u, C3).as_bool()) {
return secure_vector<uint8_t>();
}
diff --git a/src/tests/data/pubkey/sm2_invalid.vec b/src/tests/data/pubkey/sm2_invalid.vec
new file mode 100644
index 0000000..2517510
--- /dev/null
+++ b/src/tests/data/pubkey/sm2_invalid.vec
@@ -0,0 +1,60 @@
+
+Key = 8C84F7F069CD09D59543ED980CFEB77E68C7D39B9B73D359EA67C0CDB2A86B6F
+
+
+# Empty
+Ctext =
+
+# Just the SEQUENCE marker
+Ctext = 30
+
+# Truncated
+Ctext = 3072022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E51
+
+# C3 MAC too short
+Ctext = 3071022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF041F2E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F36826493860409CA311F43DB0E5E516F
+
+# C3 MAC too long
+Ctext = 3073022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04212E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F368264938696000409CA311F43DB0E5E516F
+
+# C3 and C2 fields swapped
+Ctext = 3072022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF0409CA311F43DB0E5E516F04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F368264938696
+
+# C3 MAC last bit flipped
+Ctext = 3072022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386970409CA311F43DB0E5E516F
+
+# C3 MAC first byte inverted
+Ctext = 3072022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF0420D11B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F
+
+# C1 y off by one
+Ctext = 3072022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27D004202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F
+
+# C1 x/y serialization boundary shift
+Ctext = 3073022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E500200022100C055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E2704202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F
+
+# C1 is generator point
+Ctext = 3072022032C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7022100BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A004202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F
+
+# C1 is origin (0,0)
+Ctext = 303302010002010004202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F
+
+# C1 x equals field prime
+Ctext = 3072022100FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F
+
+# C2 last bit flipped
+Ctext = 3072022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516E
+
+# C2 replaced with empty
+Ctext = 3069022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960400
+
+# Trailing byte after SEQUENCE
+Ctext = 3072022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F00
+
+# SEQUENCE with non-minimal long-form length
+Ctext = 308172022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F
+
+# INTEGER x1 with non-minimal leading zero
+Ctext = 307302220000A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF04202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F
+
+# OCTET STRING C3 with non-minimal long-form length
+Ctext = 3073022100A772DF5FFFDA85C05D9B82233B1E5F7DF7A87788504ABD4F74D265D49E5002C0022055C8A934A29DE58B31A063CDA81F12ABC712FB9ADC8988DA0FBD9FFF304E27CF0481202E1B8A86EF14243EE2E9D74E0D0E7498DFAC2F0EFA8C5883D74F3682649386960409CA311F43DB0E5E516F
diff --git a/src/tests/test_sm2.cpp b/src/tests/test_sm2.cpp
index 2ffb14a..c5d2360 100644
--- a/src/tests/test_sm2.cpp
+++ b/src/tests/test_sm2.cpp
@@ -9,6 +9,7 @@
#if defined(BOTAN_HAS_SM2)
#include "test_pubkey.h"
+ #include <botan/pubkey.h>
#include <botan/sm2.h>
#endif
@@ -108,4 +109,34 @@ BOTAN_REGISTER_TEST("pubkey", "sm2_keygen", SM2_Keygen_Tests);
#endif
+namespace {
+
+class SM2_Invalid_Ciphertexts : public Text_Based_Test {
+ public:
+ SM2_Invalid_Ciphertexts() : Text_Based_Test("pubkey/sm2_invalid.vec", "Key,Ctext") {}
+
+ bool clear_between_callbacks() const override { return false; }
+
+ Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
+ Test::Result result("SM2 invalid ciphertext");
+
+ const auto key = vars.get_req_bin("Key");
+ const auto ctext = vars.get_req_bin("Ctext");
+
+ const auto group = Botan::EC_Group::from_name("sm2p256v1");
+ const auto pkey = Botan::SM2_PrivateKey(group, Botan::EC_Scalar::deserialize(group, key).value());
+
+ Botan::PK_Decryptor_EME dec(pkey, rng(), "SM3");
+
+ result.test_throws<Botan::Exception>("Decryption should fail for invalid ciphertext",
+ [&] { dec.decrypt(ctext); });
+
+ return result;
+ }
+};
+
+BOTAN_REGISTER_TEST("pubkey", "sm2_invalid_ctext", SM2_Invalid_Ciphertexts);
+
+} // namespace
+
} // namespace Botan_Tests
+3 -1
View File
@@ -4,7 +4,9 @@ LICENSE = "BSD-2-Clause"
LIC_FILES_CHKSUM = "file://license.txt;md5=3f911cecfc74a2d9f1ead9a07bd92a6e"
SECTION = "libs"
SRC_URI = "https://botan.randombit.net/releases/Botan-${PV}.tar.xz"
SRC_URI = "https://botan.randombit.net/releases/Botan-${PV}.tar.xz \
file://CVE-2026-32877.patch \
"
SRC_URI[sha256sum] = "fde194236f6d5434f136ea0a0627f6cc9d26af8b96e9f1e1c7d8c82cd90f4f24"
S = "${UNPACKDIR}/Botan-${PV}"