From b32523491beda345f3e8177eaac143f898520b6e Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Sun, 25 Jan 2026 07:13:33 -0500 Subject: [PATCH] Avoid validation of known EC groups when decoding an explicit curve block Instead first check if the group is a known group, either a group known at compile time or something that the application registered. Only if that fails, perform the usual validation. This skips various expensive primality checks for the common case when the explicit curve is just an explicit encoding of a standard group. It also sets up for easily removing support for non-standard explicit groups; if lookup_from_params fails, instead of trying to validate the group just throw an exception. --- src/lib/pubkey/ec_group/ec_group.cpp | 62 +++++++++++--- src/lib/pubkey/ec_group/ec_inner_data.cpp | 89 +++++++++++++++++++- src/lib/pubkey/ec_group/ec_inner_data.h | 8 ++ src/tests/data/pubkey/ecc_explicit_curve.vec | 63 ++++++++++++++ src/tests/test_ecc_explicit_params.cpp | 79 +++++++++++++++++ 5 files changed, 287 insertions(+), 14 deletions(-) create mode 100644 src/tests/data/pubkey/ecc_explicit_curve.vec create mode 100644 src/tests/test_ecc_explicit_params.cpp diff --git a/src/lib/pubkey/ec_group/ec_group.cpp b/src/lib/pubkey/ec_group/ec_group.cpp index 96506afb41e..0416ccc0759 100644 --- a/src/lib/pubkey/ec_group/ec_group.cpp +++ b/src/lib/pubkey/ec_group/ec_group.cpp @@ -125,6 +125,36 @@ class EC_Group_Data_Map final { return new_group; } + std::shared_ptr lookup_from_params(const BigInt& p, + const BigInt& a, + const BigInt& b, + std::span base_pt, + const BigInt& order, + const BigInt& cofactor) { + const lock_guard_type lock(m_mutex); + + for(auto i : m_registered_curves) { + if(i->params_match(p, a, b, base_pt, order, cofactor)) { + return i; + } + } + + // Try to use the order as a hint to look up the group id + const OID oid_from_order = EC_Group::EC_group_identity_from_order(order); + if(oid_from_order.has_value()) { + auto new_group = EC_Group::EC_group_info(oid_from_order); + + // Have to check all params in the (unlikely/malicious) event of an order collision + if(new_group && new_group->params_match(p, a, b, base_pt, order, cofactor)) { + m_registered_curves.push_back(new_group); + return new_group; + } + } + + return {}; + } + + // TODO(Botan4) this entire function can be removed since OIDs will be required std::shared_ptr lookup_or_create_without_oid(const BigInt& p, const BigInt& a, const BigInt& b, @@ -252,14 +282,13 @@ std::pair, bool> EC_Group::BER_decode_EC_group(st .end_cons() .verify_end(); - if(p.bits() < 112 || p.bits() > 521 || p.is_negative()) { - throw Decoding_Error("ECC p parameter is invalid size"); + // TODO(Botan4) Require cofactor == 1 + if(cofactor <= 0 || cofactor >= 16) { + throw Decoding_Error("Invalid ECC cofactor parameter"); } - // TODO(Botan4) we can remove this check since we'll only accept pre-registered groups - auto mod_p = Barrett_Reduction::for_public_modulus(p); - if(!is_bailie_psw_probable_prime(p, mod_p)) { - throw Decoding_Error("ECC p parameter is not a prime"); + if(p.bits() < 112 || p.bits() > 521 || p.is_negative()) { + throw Decoding_Error("ECC p parameter is invalid size"); } if(a.is_negative() || a >= p) { @@ -274,17 +303,26 @@ std::pair, bool> EC_Group::BER_decode_EC_group(st throw Decoding_Error("Invalid ECC group order"); } - // TODO(Botan4) we can remove this check since we'll only accept pre-registered groups + if(auto data = ec_group_data().lookup_from_params(p, a, b, base_pt, order, cofactor)) { + return std::make_pair(data, true); + } + + /* + TODO(Botan4) the remaining code is used only to handle the case of decoding an EC_Group + which is neither a builtin group nor a group that was registered by the application. + It can all be removed and replaced with a throw + */ + + auto mod_p = Barrett_Reduction::for_public_modulus(p); + if(!is_bailie_psw_probable_prime(p, mod_p)) { + throw Decoding_Error("ECC p parameter is not a prime"); + } + auto mod_order = Barrett_Reduction::for_public_modulus(order); if(!is_bailie_psw_probable_prime(order, mod_order)) { throw Decoding_Error("Invalid ECC order parameter"); } - // TODO(Botan4) Require cofactor == 1 - if(cofactor <= 0 || cofactor >= 16) { - throw Decoding_Error("Invalid ECC cofactor parameter"); - } - const size_t p_bytes = p.bytes(); if(base_pt.size() != 1 + p_bytes && base_pt.size() != 1 + 2 * p_bytes) { throw Decoding_Error("Invalid ECC base point encoding"); diff --git a/src/lib/pubkey/ec_group/ec_inner_data.cpp b/src/lib/pubkey/ec_group/ec_inner_data.cpp index 9a0000b0d76..b1940a172ed 100644 --- a/src/lib/pubkey/ec_group/ec_inner_data.cpp +++ b/src/lib/pubkey/ec_group/ec_inner_data.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #if defined(BOTAN_HAS_LEGACY_EC_POINT) #include @@ -129,8 +130,92 @@ bool EC_Group_Data::params_match(const BigInt& p, const BigInt& g_y, const BigInt& order, const BigInt& cofactor) const { - return (this->p() == p && this->a() == a && this->b() == b && this->order() == order && - this->cofactor() == cofactor && this->g_x() == g_x && this->g_y() == g_y); + if(p != this->p()) { + return false; + } + if(a != this->a()) { + return false; + } + if(b != this->b()) { + return false; + } + if(order != this->order()) { + return false; + } + if(cofactor != this->cofactor()) { + return false; + } + if(g_x != this->g_x()) { + return false; + } + if(g_y != this->g_y()) { + return false; + } + + return true; +} + +bool EC_Group_Data::params_match(const BigInt& p, + const BigInt& a, + const BigInt& b, + std::span base_pt, + const BigInt& order, + const BigInt& cofactor) const { + if(p != this->p()) { + return false; + } + if(a != this->a()) { + return false; + } + if(b != this->b()) { + return false; + } + if(order != this->order()) { + return false; + } + if(cofactor != this->cofactor()) { + return false; + } + + const size_t field_len = this->p_bytes(); + + if(base_pt.size() == 1 + field_len && (base_pt[0] == 0x02 || base_pt[0] == 0x03)) { + // compressed + + const auto g_x = m_g_x.serialize(field_len); + const auto g_y = m_g_y.is_odd(); + + const auto sec1_x = base_pt.subspan(1, field_len); + const bool sec1_y = (base_pt[0] == 0x03); + + if(!std::ranges::equal(sec1_x, g_x)) { + return false; + } + + if(sec1_y != g_y) { + return false; + } + + return true; + } else if(base_pt.size() == 1 + 2 * field_len && base_pt[0] == 0x04) { + const auto g_x = m_g_x.serialize(field_len); + const auto g_y = m_g_y.serialize(field_len); + + const auto sec1_x = base_pt.subspan(1, field_len); + const auto sec1_y = base_pt.subspan(1 + field_len, field_len); + + if(!std::ranges::equal(sec1_x, g_x)) { + return false; + } + + if(!std::ranges::equal(sec1_y, g_y)) { + return false; + } + + return true; + } else { + throw Decoding_Error("Invalid base point encoding in explicit group"); + } } bool EC_Group_Data::params_match(const EC_Group_Data& other) const { diff --git a/src/lib/pubkey/ec_group/ec_inner_data.h b/src/lib/pubkey/ec_group/ec_inner_data.h index 4bb65177cf6..b0b8075fefa 100644 --- a/src/lib/pubkey/ec_group/ec_inner_data.h +++ b/src/lib/pubkey/ec_group/ec_inner_data.h @@ -151,6 +151,14 @@ class EC_Group_Data final : public std::enable_shared_from_this { const BigInt& order, const BigInt& cofactor) const; + // Like the other params_match but accepting the base point in encoded form + bool params_match(const BigInt& p, + const BigInt& a, + const BigInt& b, + std::span base_pt, + const BigInt& order, + const BigInt& cofactor) const; + bool params_match(const EC_Group_Data& other) const; void set_oid(const OID& oid); diff --git a/src/tests/data/pubkey/ecc_explicit_curve.vec b/src/tests/data/pubkey/ecc_explicit_curve.vec new file mode 100644 index 00000000000..ee139c4d5ba --- /dev/null +++ b/src/tests/data/pubkey/ecc_explicit_curve.vec @@ -0,0 +1,63 @@ + +# secp256r1 +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF30440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63255102010103420004CE00D94744EB2E486E86BB1A97B775BB002BBD93FF47879299A0774D5737905B11F120A5A959F5DF3E6DFA5E9EDD7D827A3D6D369548245F1AB9D37F8BC5C73E +Result = OK + +# numsp384d1 +Pubkey = 308201B53082014D06072A8648CE3D020130820140020101303C06072A8648CE3D0101023100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC330640430FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC00430FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77BB0461040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023C9F82CB4B87B4DC71E763E0663E5DBD8034ED422F04F82673330DC58D15FFA2B4A3D0BAD5D30F865BCBBF503EA66F43023100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD61EAF1EEB5D6881BEDA9D3D4C37E27A604D81F67B0E61B902010103620004C23D4E83CD9988055C1DC816F7881C14B1C915C756FE189885F67B82CF38CE69756B30FD35FB651BEC58DD8FF66018F7B5A7B269877B43FB2D5991C48059055BC6E4012C928E6D4A0761638F6A28C7157F562BF173DB9F365276291F1D5DBA78 +Result = OK + +# brainpool256t1 +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537730440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E53740420662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7020101034200043398E38269601DBADCC70F2B3EE6D77ACC16A31EA0B4CF80A073587551A1D08729C849FF3E6D6C67435D67F9495BFB27EB172C495D61002369BBE8D0A59D4726 +Result = OK + +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537930440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E53740420662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7020101034200043DE15A95F34445551D72A71D079EC862E5B6C6DDF6735E89C1AAA4FE41E6086B29E7BBA60FE13B86189FA92BA16BEEE1B6C9910A3861F67AAAA1046FFEC0C2DB +Result = p parameter is not a prime + + +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537730440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E53790420662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7020101034200046D64F7DC28A25C8A7FCDC0ADB17209600D5F51CA4704ED65542BE4C14CAA04905F7EEC921312B97DC92397292BFF38453BA33C276DC10DE1B3F376F97C48EDF9 +Result = Invalid ECC a parameter + +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537730440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537404200000000000000000000000000000000000000000000000000000000000000000044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A70201010342000477DDB835AC3BD17447F43916052A237AFA751C731E4E1931774BA941F9D6A4DB2665EDD6F67A722D21DBA21967AD949239C8AFDA0BB74163C8FC760E6CB4E572 +Result = Invalid ECC b parameter + +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537730440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E53740420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A702010103420004436AC9833C202619691DE801179A32ED82C8CE6FC7B65E3BF4B1E7D93002E18E91205D73CE90B25031EBB187DCFE6616780754DEA6AE10AD4A3B90E457187A96 +Result = Invalid ECC b parameter + +Pubkey = 308201343081ED06072A8648CE3D02013081E1020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537730440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E53740420662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A70202232803420004A4E91B6C4A84A40973ABC033BAE33F0BE201035B09547EE99854C63BA9DB50F58ABA23E502205D260E8A214C145829E3583C234060D78D3F0D6A8DBD64624C18 +Result = Invalid ECC cofactor + +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537730440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E53740420662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A702011103420004892A7459F99695DFDB613608AA529A9E385179BE8C5DD5E7A027CDC2148578D733C3CA51C255D927D83A3CA3C2F136CAACBD69EAC3EDD97B5B1C50B8047022C2 +Result = Invalid ECC cofactor + +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537730440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E53740420662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A9020101034200047E7B3DF984A44D388749CDE71DC649659488DC688C30F08DF6CB675B7908DF41A27872F84E0FD4ED90039A247DDA73B4B9F6B40A1EFF426C43B7D3742DB621E6 +Result = Invalid ECC order + +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537730440420A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E53740420662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04044104A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE022102A7ED5F6E87BAA6F0F9982A42760E35C630E5EA8ED5869BDE40783A0A5D215A9C0201010342000423FA1983DB88D73BCF3D4C04515B66A89D3E0AB6F17DF185C983128CE2BB4ADA8F98D9C4F000F8806DC2BD0523F6D623BE19C10E8E4AC2F62FD9995EACE2EAA1 +Result = Invalid ECC group order + +# secp256r1 except p + 2 +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100FFFFFFFF0000000100000000000000000000000100000000000000000000000130440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC6325510201010342000443C639578961AA7D33A9BF3B01C5F78D60F100212038BAD943DCBAF1B97D4768BCB0E52F1CCAD87CA131506B2973C0F6A256331A14D57CF0E1BF735A58746C9D +Result = ECC p parameter is not a prime + +# secp256r1 except order too large +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF30440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5022101FFFFFFFE00000001FFFFFFFFFFFFFFFFBCE6FAAEA7179E84F3B9CAC2FC632550020101034200040FD6C6B52EF3D6C94871551A59A4A79DF50B8570CF99060537D9871FB212582B190B1C90695C18467EF9B8A0E661DE51FB9858E92FB3809DB6440E87094EA0EA +Result = Invalid ECC order + +# secp256r1 except order is negative +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF30440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F50221FF00000000FFFFFFFF00000000000000004319055258E8617B0C46353D039CDAAF02010103420004896F4BB3988F7924C5FBB7E1D74A911E3C8238288CC4386AB1A486079FA983BE4B58ED1FB49FCBD9F4EBBFC13DB017FC7BE16370209722746B93E3A8D22CF6D4 +Result = Invalid ECC group order + +# secp256r1 except cofactor is set to 17 +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF30440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC6325510201110342000438378C6590DAF376BBD6E307521EF2CF38209E180EE01B64F0384AD0F2139692DDBF761F3BC66AF3B983E30400BE1B6076FD8453E8470588E86D828F1837787D +Result = Invalid ECC cofactor + +# secp256r1 except cofactor is zero +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF30440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63255102010003420004CE92F24CA68EFD9DBF9E85D15EF6C718C75C5FBB3E51D2A31A538982A25898B44E199C37D94F371EEF4123C61C02C680D9AB48ECE6C4C5C7F51A52300A5199C2 +Result = Invalid ECC cofactor + +# secp256r1 except invalid group generator +Pubkey = 308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF30440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2974FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63255102010103420004F25EDB253BF46F789C1180223B5F882C11F6481F11C2AD9EEA81E88446B5A5D0208D81AA408165899F2408AA0888D894BF6A67E958B692653C9ECFA932B89DBC +Result = Invalid ECC base point + + diff --git a/src/tests/test_ecc_explicit_params.cpp b/src/tests/test_ecc_explicit_params.cpp new file mode 100644 index 00000000000..493febac02a --- /dev/null +++ b/src/tests/test_ecc_explicit_params.cpp @@ -0,0 +1,79 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" + +#if defined(BOTAN_HAS_ECDSA) + #include + #include + #include +#endif + +namespace Botan_Tests { + +namespace { + +#if defined(BOTAN_HAS_ECDSA) + +class ECC_Explicit_Curve_Tests final : public Text_Based_Test { + public: + ECC_Explicit_Curve_Tests() : Text_Based_Test("pubkey/ecc_explicit_curve.vec", "Pubkey,Result") {} + + Test::Result run_one_test(const std::string& /*unused*/, const VarMap& vars) override { + Test::Result result("ECC explicit curve validation"); + + const auto pubkey = vars.get_req_bin("Pubkey"); + const auto expected_result = vars.get_req_str("Result"); + + try { + auto pk = Botan::X509::load_key(pubkey); + + const auto* ecdsa = dynamic_cast(pk.get()); + if(ecdsa != nullptr) { + result.test_success("Returned key was ECDSA"); + + auto used_explicit = ecdsa->domain().used_explicit_encoding(); + + result.confirm("Loaded ECC key marked as an explicit encoding", used_explicit); + } else { + result.test_failure("Returned key was some other type"); + } + + if(expected_result == "OK") { + result.test_success("Accepted valid explicit curve parameters"); + } else { + result.test_failure("Accepted invalid explicit curve parameters"); + } + } catch(Botan::Not_Implemented& e) { + // Can happen if pcurves_generic is not in the build + const std::string err(e.what()); + result.confirm("Expected error", err.find("is not supported in this build config") != std::string::npos); + } catch(Botan::Exception& e) { + const std::string err(e.what()); + if(expected_result == "OK") { + result.test_failure("Rejected valid explicit curve parameters", err); + } else { + result.test_success("Rejected invalid explicit curve parameters"); + + if(err.find(expected_result) != std::string::npos) { + result.test_success("Rejection error matches expected"); + } else { + result.test_failure("Rejection failure other than what was expected", err); + } + } + } + + return result; + } +}; + +BOTAN_REGISTER_TEST("pubkey", "ecc_explicit_curve", ECC_Explicit_Curve_Tests); + +#endif + +} // namespace + +} // namespace Botan_Tests