diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6882dea5c..396d6d22c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -82,13 +82,24 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + - name: Expose Android NDK env + shell: bash + run: | + echo "ANDROID_NDK_HOME=$ANDROID_NDK_LATEST_HOME" >> "$GITHUB_ENV" + echo "ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME" >> "$GITHUB_ENV" + - name: Cache vcpkg + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/vcpkg_cache + key: vcpkg-${{ matrix.target }}-${{ hashFiles('.github/workflows/*', 'vcpkg.json', 'vcpkg-ports/**', 'CMakeLists.txt', '**/CMakeLists.txt', 'CMakePresets.json') }} - name: Prepare vcpkg uses: lukka/run-vcpkg@v11 with: - vcpkgGitCommitId: 031ad89ce6c575df35a8e58707ad2c898446c63e + vcpkgGitCommitId: 98e7cd3a7ba579efc543f8854af800d033031eae vcpkgJsonGlob: ./vcpkg.json runVcpkgInstall: true env: + VCPKG_BINARY_SOURCES: clear;files,${{ github.workspace }}/vcpkg_cache,readwrite VCPKG_DEFAULT_TRIPLET: ${{ matrix.triplet }} - name: Build run: | @@ -179,14 +190,20 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + - name: Cache vcpkg + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/vcpkg_cache + key: vcpkg-${{ matrix.toolset }}-${{ matrix.platform }}-${{ hashFiles('.github/workflows/*', 'vcpkg.json', 'vcpkg-ports/**', 'CMakeLists.txt', '**/CMakeLists.txt', 'CMakePresets.json') }} - name: Prepare vcpkg uses: lukka/run-vcpkg@v11 with: - vcpkgGitCommitId: 031ad89ce6c575df35a8e58707ad2c898446c63e + vcpkgGitCommitId: 98e7cd3a7ba579efc543f8854af800d033031eae vcpkgJsonGlob: ./vcpkg.json runVcpkgInstall: true runVcpkgFormatString: "[`install`, `--recurse`, `--clean-after-build`, `--x-install-root`, `$[env.VCPKG_INSTALLED_DIR]`, `--triplet`, `$[env.VCPKG_DEFAULT_TRIPLET]`, `--x-feature`, `tests`]" env: + VCPKG_BINARY_SOURCES: clear;files,${{ github.workspace }}/vcpkg_cache,readwrite VCPKG_DEFAULT_TRIPLET: ${{ matrix.platform }}-windows VCPKG_INSTALLED_DIR: ${{ github.workspace }}/vcpkg_installed_${{ matrix.platform }} - name: Install dependencies diff --git a/examples/android/build.gradle b/examples/android/build.gradle index 8c27726bb..f19275001 100644 --- a/examples/android/build.gradle +++ b/examples/android/build.gradle @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.12.0' + classpath 'com.android.tools.build:gradle:8.12.2' } } diff --git a/examples/ios/libdigidocpp/Info.plist b/examples/ios/libdigidocpp/Info.plist index ff9a875d5..547dd08f4 100644 --- a/examples/ios/libdigidocpp/Info.plist +++ b/examples/ios/libdigidocpp/Info.plist @@ -5,8 +5,6 @@ CFBundleDocumentTypes - CFBundleTypeIconFiles - CFBundleTypeName DigiDoc signed document CFBundleTypeRole diff --git a/libdigidocpp.wxs b/libdigidocpp.wxs index 918195bd7..306fc336f 100644 --- a/libdigidocpp.wxs +++ b/libdigidocpp.wxs @@ -80,10 +80,6 @@ - - - - @@ -105,7 +101,7 @@ - + diff --git a/prepare_osx_build_environment.sh b/prepare_osx_build_environment.sh index 69e12ed00..ae2fad9a5 100755 --- a/prepare_osx_build_environment.sh +++ b/prepare_osx_build_environment.sh @@ -1,7 +1,7 @@ #!/bin/sh set -e -OPENSSL_DIR=openssl-3.5.1 +OPENSSL_DIR=openssl-3.5.2 XMLSEC_DIR=xmlsec1-1.3.7 ARGS="$@" @@ -59,6 +59,7 @@ function xmlsec { cd ${XMLSEC_DIR} patch -Np1 -i ../vcpkg-ports/xmlsec/xmlsec1-1.3.5.legacy.patch patch -Np1 -i ../vcpkg-ports/xmlsec/xmlsec1-1.3.7.rsapss.patch + patch -Np1 -i ../vcpkg-ports/xmlsec/xmlsec1-1.3.7.ecdsa-sig.patch case "${ARGS}" in *iphone*) CONFIGURE="--host=aarch64-apple-darwin --enable-static --disable-shared --without-libxslt" ;; *) CONFIGURE="--disable-static --enable-shared" ;; diff --git a/src/XMLDocument.h b/src/XMLDocument.h index 33dda5a26..33d4bf617 100644 --- a/src/XMLDocument.h +++ b/src/XMLDocument.h @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -45,7 +46,7 @@ static std::vector from_base64(std::string_view data) std::vector result(EVP_DECODE_LENGTH(data.size()), 0); size_t dataPos = 0; int size = 0; - auto ctx = make_unique_ptr(EVP_ENCODE_CTX_new(), EVP_ENCODE_CTX_free); + auto ctx = make_unique_ptr(EVP_ENCODE_CTX_new()); EVP_DecodeInit(ctx.get()); for(auto pos = data.find_first_of(whitespace); @@ -71,7 +72,7 @@ static std::vector from_base64(std::string_view data) static std::string to_base64(const std::vector &data) { std::string result(EVP_ENCODE_LENGTH(data.size()), 0); - auto ctx = make_unique_ptr(EVP_ENCODE_CTX_new(), EVP_ENCODE_CTX_free); + auto ctx = make_unique_ptr(EVP_ENCODE_CTX_new()); EVP_EncodeInit(ctx.get()); int size{}; if(EVP_EncodeUpdate(ctx.get(), (unsigned char*)result.data(), &size, data.data(), int(data.size())) < 1) @@ -305,19 +306,27 @@ struct XMLDocument: public unique_free_t, public XMLNode static XMLDocument openStream(std::istream &is, const XMLName &name = {}, bool hugeFile = false) { - auto ctxt = make_unique_ptr(xmlCreateIOParserCtxt(nullptr, nullptr, [](void *context, char *buffer, int len) -> int { + auto ctxt = make_unique_ptr(xmlCreateIOParserCtxt(nullptr, nullptr, [](void *context, char *buffer, int len) -> int { auto *is = static_cast(context); is->read(buffer, len); return is->good() || is->eof() ? int(is->gcount()) : -1; - }, nullptr, &is, XML_CHAR_ENCODING_NONE), xmlFreeParserCtxt); - ctxt->linenumbers = 1; + }, nullptr, &is, XML_CHAR_ENCODING_NONE)); +#if VERSION_CHECK(XMLSEC_VERSION_MAJOR, XMLSEC_VERSION_MINOR, XMLSEC_VERSION_SUBMINOR) >= VERSION_CHECK(1, 3, 0) + ctxt->options |= xmlSecParserGetDefaultOptions(); +#else ctxt->options |= XML_PARSE_NOENT|XML_PARSE_DTDLOAD|XML_PARSE_DTDATTR|XML_PARSE_NONET; +#endif ctxt->loadsubset |= XML_DETECT_IDS|XML_COMPLETE_ATTRS; if(hugeFile) ctxt->options |= XML_PARSE_HUGE; auto result = xmlParseDocument(ctxt.get()); if(result != 0 || !ctxt->wellFormed) - THROW("%s", ctxt->lastError.message); + { + if(const xmlError *lastError = xmlCtxtGetLastError(ctxt.get())) + THROW("%s", lastError->message); + else + THROW("Failed to parse XML document from stream"); + } return {ctxt->myDoc, name}; } @@ -358,11 +367,11 @@ struct XMLDocument: public unique_free_t, public XMLNode } else if(!algo.empty()) THROW("Unsupported canonicalization method '%.*s'", int(algo.size()), algo.data()); - auto buf = make_unique_ptr(xmlOutputBufferCreateIO([](void *context, const char *buffer, int len) { + auto buf = make_unique_ptr(xmlOutputBufferCreateIO([](void *context, const char *buffer, int len) { auto *digest = static_cast(context); digest->update(pcxmlChar(buffer), size_t(len)); return len; - }, nullptr, const_cast(&digest), nullptr), xmlOutputBufferClose); + }, nullptr, const_cast(&digest), nullptr)); int size = xmlC14NExecute(get(), [](void *root, xmlNodePtr node, xmlNodePtr parent) constexpr noexcept { if(root == node) return 1; @@ -396,14 +405,14 @@ struct XMLDocument: public unique_free_t, public XMLNode void validateSchema(const std::string &schemaPath) const { - auto parser = make_unique_ptr(xmlSchemaNewParserCtxt(schemaPath.c_str()), xmlSchemaFreeParserCtxt); + auto parser = make_unique_ptr(xmlSchemaNewParserCtxt(schemaPath.c_str())); if(!parser) THROW("Failed to create schema parser context %s", schemaPath.c_str()); xmlSchemaSetParserErrors(parser.get(), schemaValidationError, schemaValidationWarning, nullptr); - auto schema = make_unique_ptr(xmlSchemaParse(parser.get()), xmlSchemaFree); + auto schema = make_unique_ptr(xmlSchemaParse(parser.get())); if(!schema) THROW("Failed to parse schema %s", schemaPath.c_str()); - auto validate = make_unique_ptr(xmlSchemaNewValidCtxt(schema.get()), xmlSchemaFreeValidCtxt); + auto validate = make_unique_ptr(xmlSchemaNewValidCtxt(schema.get())); if(!validate) THROW("Failed to create schema validation context %s", schemaPath.c_str()); Exception e(EXCEPTION_PARAMS("Failed to XML with schema")); @@ -414,12 +423,12 @@ struct XMLDocument: public unique_free_t, public XMLNode static bool verifySignature(XMLNode signature, [[maybe_unused]] Exception *e = {}) noexcept { - auto mngr = make_unique_ptr(xmlSecKeysMngrCreate(), xmlSecKeysMngrDestroy); + auto mngr = make_unique_ptr(xmlSecKeysMngrCreate()); if(!mngr) return false; if(xmlSecCryptoAppDefaultKeysMngrInit(mngr.get()) < 0) return false; - auto ctx = make_unique_ptr(xmlSecDSigCtxCreate(mngr.get()), xmlSecDSigCtxDestroy); + auto ctx = make_unique_ptr(xmlSecDSigCtxCreate(mngr.get())); if(!ctx) return false; ctx->keyInfoReadCtx.flags |= XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS; diff --git a/src/crypto/X509Cert.cpp b/src/crypto/X509Cert.cpp index a2a2d5f62..6bc7f2fe3 100644 --- a/src/crypto/X509Cert.cpp +++ b/src/crypto/X509Cert.cpp @@ -29,15 +29,18 @@ using namespace digidoc; using namespace std; +// RFC 3739: NameRegistrationAuthorities is SEQUENCE OF GeneralName +using NameRegistrationAuthorities = STACK_OF(GENERAL_NAME); + /** * SemanticsInformation ::= SEQUENCE { * semanticsIdentifier OBJECT IDENTIFIER OPTIONAL, * nameRegistrationAuthorities NameRegistrationAuthorities OPTIONAL * } */ -using SemanticsInformation = struct SemanticsInformation_st { +struct SemanticsInformation { ASN1_OBJECT *semanticsIdentifier; - //NameRegistrationAuthorities nameRegistrationAuthorities + NameRegistrationAuthorities *nameRegistrationAuthorities; }; DECLARE_ASN1_FUNCTIONS(SemanticsInformation) @@ -52,7 +55,7 @@ DECLARE_ASN1_FUNCTIONS(QcType) * statementId OBJECT IDENTIFIER, * statementInfo ANY DEFINED BY statementId OPTIONAL} */ -using QCStatement = struct QCStatement_st { +struct QCStatement { ASN1_OBJECT *statementId; #ifndef TEMPLATE ASN1_TYPE *statementInfo; @@ -297,8 +300,7 @@ string X509Cert::serial() const return {}; if(auto bn = make_unique_ptr(ASN1_INTEGER_to_BN(X509_get_serialNumber(cert.get()), nullptr))) { - auto openssl_free = [](char *data) { OPENSSL_free(data); }; - if(auto str = unique_ptr(BN_bn2dec(bn.get()), openssl_free)) + if(auto str = make_unique_ptr(BN_bn2dec(bn.get()), [](char *data) { OPENSSL_free(data); })) return str.get(); } return {}; @@ -316,15 +318,19 @@ string X509Cert::issuerName(const string &obj) const return toString(obj); } +template +constexpr auto X509Cert::extension(int nid) const noexcept +{ + return make_unique_cast(cert ? X509_get_ext_d2i(cert.get(), nid, nullptr, nullptr) : nullptr); +} + /** * Returns current certificate key usage bits */ vector X509Cert::keyUsage() const { vector usage; - if(!cert) - return usage; - auto keyusage = make_unique_cast(X509_get_ext_d2i(cert.get(), NID_key_usage, nullptr, nullptr)); + auto keyusage = extension(NID_key_usage); if(!keyusage) return usage; @@ -342,9 +348,7 @@ vector X509Cert::keyUsage() const vector X509Cert::certificatePolicies() const { vector pol; - if(!cert) - return pol; - auto cp = make_unique_cast(X509_get_ext_d2i(cert.get(), NID_certificate_policies, nullptr, nullptr)); + auto cp = extension(NID_certificate_policies); if(!cp) return pol; for(int i = 0; i < sk_POLICYINFO_num(cp.get()); ++i) @@ -375,12 +379,8 @@ vector X509Cert::qcStatements() const if(oid == QC_SYNTAX2) { #ifndef TEMPLATE - if(!s->statementInfo) - continue; - auto si = make_unique_cast(ASN1_item_unpack(s->statementInfo->value.sequence, ASN1_ITEM_rptr(SemanticsInformation))); - if(!si) - continue; - result.push_back(toOID(si->semanticsIdentifier)); + if(auto si = make_unique_cast(ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(SemanticsInformation), s->statementInfo))) + result.push_back(toOID(si->semanticsIdentifier)); #else result.push_back(toOID(s->statementInfo.semanticsInformation->semanticsIdentifier)); #endif @@ -388,17 +388,13 @@ vector X509Cert::qcStatements() const else if(oid == QC_QCT) { #ifndef TEMPLATE - if(!s->statementInfo) - continue; - auto qct = make_unique_cast(ASN1_item_unpack(s->statementInfo->value.sequence, ASN1_ITEM_rptr(QcType))); + auto qct = make_unique_cast(ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(QcType), s->statementInfo)); if(!qct) continue; for(int j = 0; j < sk_ASN1_OBJECT_num(qct.get()); ++j) - { result.push_back(toOID(sk_ASN1_OBJECT_value(qct.get(), j))); #else #endif - } } else result.push_back(std::move(oid)); @@ -487,9 +483,7 @@ X509* X509Cert::handle() const */ bool X509Cert::isCA() const { - if(!cert) - return false; - auto cons = make_unique_cast(X509_get_ext_d2i(cert.get(), NID_basic_constraints, nullptr, nullptr)); + auto cons = extension(NID_basic_constraints); return cons && cons->ca > 0; } @@ -532,7 +526,11 @@ X509Cert& X509Cert::operator =(X509Cert &&other) noexcept = default; */ bool X509Cert::operator ==(X509 *other) const { - return operator==(X509Cert(other)); + if(cert.get() == other) + return true; + if(!cert || !other) + return false; + return X509_cmp(cert.get(), other) == 0; } /** @@ -540,11 +538,7 @@ bool X509Cert::operator ==(X509 *other) const */ bool X509Cert::operator ==(const X509Cert &other) const { - if(cert == other.cert) - return true; - if(!cert || !other.cert) - return false; - return X509_cmp(cert.get(), other.cert.get()) == 0; + return operator==(other.cert.get()); } /** @@ -556,8 +550,8 @@ bool X509Cert::operator !=(const X509Cert &other) const } ASN1_SEQUENCE(SemanticsInformation) = { - ASN1_OPT(SemanticsInformation, semanticsIdentifier, ASN1_OBJECT) - //ASN1_OPT(SemanticsInformation, nameRegistrationAuthorities, NameRegistrationAuthorities) + ASN1_OPT(SemanticsInformation, semanticsIdentifier, ASN1_OBJECT), + ASN1_SEQUENCE_OF_OPT(SemanticsInformation, nameRegistrationAuthorities, GENERAL_NAME) } ASN1_SEQUENCE_END(SemanticsInformation) IMPLEMENT_ASN1_FUNCTIONS(SemanticsInformation) diff --git a/src/crypto/X509Cert.h b/src/crypto/X509Cert.h index c1b44f341..f6b819628 100644 --- a/src/crypto/X509Cert.h +++ b/src/crypto/X509Cert.h @@ -111,6 +111,8 @@ namespace digidoc static std::string toOID(ASN1_OBJECT *obj); template std::string toString(const std::string &obj) const; + template + constexpr auto extension(int nid) const noexcept; std::shared_ptr cert; }; } diff --git a/vcpkg-ports/xmlsec/portfile.cmake b/vcpkg-ports/xmlsec/portfile.cmake index 8bd871339..7871dd094 100644 --- a/vcpkg-ports/xmlsec/portfile.cmake +++ b/vcpkg-ports/xmlsec/portfile.cmake @@ -11,6 +11,7 @@ vcpkg_from_github( pkgconfig_fixes.patch xmlsec1-1.3.5.legacy.patch xmlsec1-1.3.7.rsapss.patch + xmlsec1-1.3.7.ecdsa-sig.patch ) file(COPY "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt" DESTINATION "${SOURCE_PATH}") diff --git a/vcpkg-ports/xmlsec/vcpkg.json b/vcpkg-ports/xmlsec/vcpkg.json index 1adcff2da..f6dc95e03 100644 --- a/vcpkg-ports/xmlsec/vcpkg.json +++ b/vcpkg-ports/xmlsec/vcpkg.json @@ -1,6 +1,7 @@ { "name": "xmlsec", "version": "1.3.7", + "port-version": 1, "description": "XML Security Library is a C library based on LibXML2. The library supports major XML security standards.", "homepage": "https://www.aleksey.com/xmlsec/", "license": "X11 AND MPL-1.1", diff --git a/vcpkg-ports/xmlsec/xmlsec1-1.3.7.ecdsa-sig.patch b/vcpkg-ports/xmlsec/xmlsec1-1.3.7.ecdsa-sig.patch new file mode 100644 index 000000000..97bc6da80 --- /dev/null +++ b/vcpkg-ports/xmlsec/xmlsec1-1.3.7.ecdsa-sig.patch @@ -0,0 +1,326 @@ +From 5a4eb6c10cb27718bc3bbe59998efaca83294cd9 Mon Sep 17 00:00:00 2001 +From: lsh123 +Date: Thu, 14 Aug 2025 08:21:43 -0700 +Subject: [PATCH] (xmlsec-openssl, xmlsec-gnutls, xmlsec-mscng) Added support + for longer than expected DSA and ECDSA sigantures to support broken Java + implementations. (#943) + +diff --git a/src/gnutls/signatures.c b/src/gnutls/signatures.c +index 107ce5e7..d0b8a55e 100644 +--- a/src/gnutls/signatures.c ++++ b/src/gnutls/signatures.c +@@ -591,11 +591,18 @@ xmlSecGnuTLSToDer(const gnutls_datum_t* src, gnutls_datum_t* dst, xmlSecSize siz + + + /* check size: we expect the r and s to be the same size and match the size of +- * the key (RFC 6931); however some implementations (e.g. Java) cut leading zeros: +- * https://github.com/lsh123/xmlsec/issues/228 */ +- if((src->size < 2 * size) && (src->size % 2 == 0)) { ++ * the key (RFC 6931) */ ++ if(src->size == 2 * size) { ++ /* good, do nothing */ ++ } else if((src->size < 2 * size) && (src->size % 2 == 0)) { ++ /* however some implementations (e.g. Java) cut leading zeros: ++ * https://github.com/lsh123/xmlsec/issues/228 */ + size = src->size / 2; +- } else if(src->size != 2 * size) { ++ } else if((src->size > 2 * size) && (src->size % 2 == 0)) { ++ /* however some implementations (e.g. Java) add leading zeros: ++ * https://github.com/lsh123/xmlsec/issues/941*/ ++ size = src->size / 2; ++ } else { + xmlSecInternalError3("Invalid signature size", NULL, + "actual=%u; expected=" XMLSEC_SIZE_FMT, src->size, 2 * size); + return(-1); +diff --git a/src/mscng/signatures.c b/src/mscng/signatures.c +index 16253b93..d4c03d12 100644 +--- a/src/mscng/signatures.c ++++ b/src/mscng/signatures.c +@@ -424,7 +424,7 @@ static int xmlSecMSCngSignatureSetKeyReq(xmlSecTransformPtr transform, xmlSecKe + + /* + * https://www.w3.org/TR/xmldsig-core1/#sec-ECDSA +-* ++* + * The output of the ECDSA algorithm consists of a pair of integers usually + * referred by the pair(r, s).The signature value consists of the base64 + * encoding of the concatenation of two octet - streams that respectively result +@@ -464,32 +464,60 @@ xmlSecMSCngSignatureFixBrokenJava(xmlSecMSCngSignatureCtxPtr ctx, + return(0); + } + +- /* check the size */ ++ /* check the size: we expect the DSA/ECDSA r and s to be the same size and either have ++ * fixed size (DSA) or match the size of the key (ECDSA) */ + if (2 * halfSize == dataSize) { + return(0); + } +- else if ((dataSize > 2 * halfSize) || (dataSize % 2 != 0)) { +- xmlSecInternalError3("xmlSecOpenSSLEvpSignatureEcdsaHalfLen", NULL, +- "expectedSignLen=" XMLSEC_SIZE_FMT "; actualSignLen=" XMLSEC_SIZE_FMT, 2 * halfSize, dataSize); +- return(-1); +- } + +- /* let's fix it! */ +- res = (xmlSecByte*)xmlMalloc(2 * halfSize); +- if (res == NULL) { +- xmlSecMallocError(2 * halfSize, NULL); ++ if ((dataSize < 2 * halfSize) && (dataSize % 2 == 0)) { ++ /* however some implementations (e.g. Java) cut leading zeros: ++ * https://github.com/lsh123/xmlsec/issues/228 */ ++ ++ /* let's fix it! */ ++ res = (xmlSecByte*)xmlMalloc(2 * halfSize); ++ if (res == NULL) { ++ xmlSecMallocError(2 * halfSize, NULL); ++ return(-1); ++ } ++ memset(res, 0, 2 * halfSize); ++ ++ /* add zeros at the beggining of both r and s */ ++ offset = (2 * halfSize - dataSize) / 2; ++ memcpy(res + offset, data, dataSize / 2); ++ memcpy(res + halfSize + offset, data + dataSize / 2, dataSize / 2); ++ ++ /* success */ ++ (*out) = res; ++ (*outSize) = 2 * halfSize; ++ } else if ((dataSize > 2 * halfSize) && (dataSize % 2 == 0)) { ++ /* however some implementations (e.g. Java) add leading zeros: ++ * https://github.com/lsh123/xmlsec/issues/941 */ ++ ++ /* let's fix it! */ ++ res = (xmlSecByte*)xmlMalloc(2 * halfSize); ++ if (res == NULL) { ++ xmlSecMallocError(2 * halfSize, NULL); ++ return(-1); ++ } ++ memset(res, 0, 2 * halfSize); ++ ++ /* remove zeros at the beggining of both r and s (note: we don't check if those ++ * are actually zeros, just hope for the best) */ ++ offset = (dataSize - 2 * halfSize) / 2; ++ memcpy(res, data + offset, halfSize); ++ memcpy(res + halfSize, data + dataSize / 2 + offset, halfSize); ++ ++ /* success */ ++ (*out) = res; ++ (*outSize) = 2 * halfSize; ++ } else { ++ xmlSecInternalError3("xmlSecMSCngSignatureFixBrokenJava", NULL, ++ "expectedSignLen=" XMLSEC_SIZE_FMT "; actualSignLen=" XMLSEC_SIZE_FMT, 2 * halfSize, dataSize); + return(-1); + } +- memset(res, 0, 2 * halfSize); +- +- /* add zeros at the beggining of both r and s */ +- offset = (2 * halfSize - dataSize) / 2; +- memcpy(res + offset, data, dataSize / 2); +- memcpy(res + halfSize + offset, data + dataSize / 2, dataSize / 2); + +- /* success */ +- (*out) = res; +- (*outSize) = 2 * halfSize; ++ /* done */ + return(0); + } + +@@ -538,8 +566,7 @@ xmlSecMSCngSignatureVerify(xmlSecTransformPtr transform, + pPaddingInfo = &pssPadingInfo; + } else { + /* we expect the DSA/ECDSA r and s to be the same size and either have fixed size (DSA) or match +- * the size of the key (ECDSA); however some implementations (e.g. Java) cut leading zeros: +- * https://github.com/lsh123/xmlsec/issues/228 */ ++ * the size of the key (ECDSA); however some implementations (e.g. Java) cut or add leading zeros */ + ret = xmlSecMSCngSignatureFixBrokenJava(ctx, data, dataSize, (const xmlSecByte**)&fixedData, &fixedDataSize); + if (ret < 0) { + xmlSecInternalError("xmlSecMSCngSignatureFixBrokenJava", xmlSecTransformGetName(transform)); +diff --git a/src/openssl/signatures.c b/src/openssl/signatures.c +index 0c661470..bc8f7b57 100644 +--- a/src/openssl/signatures.c ++++ b/src/openssl/signatures.c +@@ -1635,12 +1635,19 @@ xmlSecOpenSSLEvpSignatureDsa_XmlDSig2OpenSSL(const xmlSecTransformId transformId + } + + /* check size: we expect the r and s to be the same size and match the size of +- * the key (RFC 6931); however some implementations (e.g. Java) cut leading zeros: +- * https://github.com/lsh123/xmlsec/issues/228 */ ++ * the key (RFC 6931) */ + XMLSEC_SAFE_CAST_SIZE_TO_INT(dataSize, signLen, goto done, NULL); +- if((signLen < 2 * signHalfLen) && (signLen % 2 == 0)) { ++ if(signLen == 2 * signHalfLen) { ++ /* good, do nothing */ ++ } else if((signLen < 2 * signHalfLen) && (signLen % 2 == 0)) { ++ /* however some implementations (e.g. Java) cut leading zeros: ++ * https://github.com/lsh123/xmlsec/issues/228 */ + signHalfLen = signLen / 2; +- } else if(signLen != 2 * signHalfLen) { ++ } else if((signLen > 2 * signHalfLen) && (signLen % 2 == 0)) { ++ /* however some implementations (e.g. Java) add leading zeros: ++ * https://github.com/lsh123/xmlsec/issues/941 */ ++ signHalfLen = signLen / 2; ++ } else { + xmlSecInternalError3("xmlSecOpenSSLEvpSignatureDsaHalfLen", NULL, + "signLen=%d; signHalfLen=%d", signLen, signHalfLen); + goto done; +@@ -1858,12 +1865,19 @@ xmlSecOpenSSLEvpSignatureEcdsa_XmlDSig2OpenSSL(xmlSecSize keySizeBits, const xml + XMLSEC_SAFE_CAST_SIZE_TO_INT((keySizeBits + 7) / 8, signHalfLen, goto done, NULL); + + /* check size: we expect the r and s to be the same size and match the size of +- * the key (RFC 6931); however some implementations (e.g. Java) cut leading zeros: +- * https://github.com/lsh123/xmlsec/issues/228 */ ++ * the key (RFC 6931) */ + XMLSEC_SAFE_CAST_SIZE_TO_INT(dataSize, signLen, goto done, NULL); +- if((signLen < 2 * signHalfLen) && (signLen % 2 == 0)) { +- signHalfLen = signLen / 2; +- } else if(signLen != 2 * signHalfLen) { ++ if(signLen == 2 * signHalfLen) { ++ /* good, do nothing */ ++ } else if((signLen < 2 * signHalfLen) && (signLen % 2 == 0)) { ++ /* however some implementations (e.g. Java) cut leading zeros: ++ * https://github.com/lsh123/xmlsec/issues/228 */ ++ signHalfLen = signLen / 2; ++ } else if((signLen > 2 * signHalfLen) && (signLen % 2 == 0)) { ++ /* however some implementations (e.g. Java) add leading zeros: ++ * https://github.com/lsh123/xmlsec/issues/941 */ ++ signHalfLen = signLen / 2; ++ } else { + xmlSecInternalError3("xmlSecOpenSSLEvpSignatureEcdsaHalfLen", NULL, + "signLen=%d; signHalfLen=%d", signLen, signHalfLen); + goto done; +diff --git a/src/openssl/signatures_legacy.c b/src/openssl/signatures_legacy.c +index c7f53071..a57fd535 100644 +--- a/src/openssl/signatures_legacy.c ++++ b/src/openssl/signatures_legacy.c +@@ -759,12 +759,19 @@ xmlSecOpenSSLSignatureLegacyEcdsaVerify(xmlSecOpenSSLSignatureLegacyCtxPtr ctx, + } + + /* check size: we expect the r and s to be the same size and match the size of +- * the key (RFC 6931); however some implementations (e.g. Java) cut leading zeros: +- * https://github.com/lsh123/xmlsec/issues/228 */ ++ * the key (RFC 6931) */ + XMLSEC_OPENSSL_SAFE_CAST_SIZE_TO_SIZE_T(signSize, signLen, goto done, NULL); +- if((signLen < 2 * signHalfLen) && (signLen % 2 == 0)) { ++ if(signLen == 2 * signHalfLen) { ++ /* good, do nothing */ ++ } else if((signLen < 2 * signHalfLen) && (signLen % 2 == 0)) { ++ /* however some implementations (e.g. Java) cut leading zeros: ++ * https://github.com/lsh123/xmlsec/issues/228 */ + signHalfLen = signLen / 2; +- } else if(signLen != 2 * signHalfLen) { ++ } else if((signLen > 2 * signHalfLen) && (signLen % 2 == 0)) { ++ /* however some implementations (e.g. Java) add leading zeros: ++ * https://github.com/lsh123/xmlsec/issues/941*/ ++ signHalfLen = signLen / 2; ++ } else { + xmlSecInvalidDataError("Signature length doesn't match key size", NULL); + goto done; + } +diff --git a/tests/aleksey-xmldsig-01/enveloping-sha256-ecdsa-sha256_padded.xml b/tests/aleksey-xmldsig-01/enveloping-sha256-ecdsa-sha256_padded.xml +new file mode 100644 +index 00000000..9ccd9f09 +--- /dev/null ++++ b/tests/aleksey-xmldsig-01/enveloping-sha256-ecdsa-sha256_padded.xml +@@ -0,0 +1,80 @@ ++ ++ ++ ++ ++ ++ ++ ++ iDhYt78o294fA6pzQ7k44+eejrQMi+WX3l3UrUdtL1Q= ++ ++ ++ AHqXLYwxGqMPCXTruP6QrFfFsWAbX3lhi2A+dQkVXrlIAONNoCh2v61el13WO9ih ++LG9iOELiGYlOAfS0SZhYQ9hm ++ ++ ++MIID9zCCA2CgAwIBAgIJAK+ii7kzrdqsMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD ++VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1MIFNlY3Vy ++aXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2VjKTEQMA4G ++A1UECxMHUm9vdCBDQTEWMBQGA1UEAxMNQWxla3NleSBTYW5pbjEhMB8GCSqGSIb3 ++DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tMCAXDTE0MDUyMzE3NTA1OVoYDzIxMTQw ++NDI5MTc1MDU5WjCBrjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWEx ++PTA7BgNVBAoTNFhNTCBTZWN1cml0eSBMaWJyYXJ5IChodHRwOi8vd3d3LmFsZWtz ++ZXkuY29tL3htbHNlYykxEDAOBgNVBAsTB1Jvb3QgQ0ExFjAUBgNVBAMTDUFsZWtz ++ZXkgU2FuaW4xITAfBgkqhkiG9w0BCQEWEnhtbHNlY0BhbGVrc2V5LmNvbTCBnzAN ++BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtY4MCNj/qrOzVuex1BD/PuCYTDDOLLVj ++tpKXQteQPqy0kgMwuQgRwdNnICIHQbnFKL40XoyACJVWKM7b0LkvWJNeyVzXPqEE ++9ZPmNxWGUjVcr7powT7v8V7S2QflUnr8ZvR4XWwkZJ9EYKNhenijgJ5yYDrXCWdv ++C+fnjBjv2LcCAwEAAaOCARcwggETMB0GA1UdDgQWBBQGtaSsp6p1ROoVnE/fBYNP ++ah7+CzCB4wYDVR0jBIHbMIHYgBQGtaSsp6p1ROoVnE/fBYNPah7+C6GBtKSBsTCB ++rjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExPTA7BgNVBAoTNFhN ++TCBTZWN1cml0eSBMaWJyYXJ5IChodHRwOi8vd3d3LmFsZWtzZXkuY29tL3htbHNl ++YykxEDAOBgNVBAsTB1Jvb3QgQ0ExFjAUBgNVBAMTDUFsZWtzZXkgU2FuaW4xITAf ++BgkqhkiG9w0BCQEWEnhtbHNlY0BhbGVrc2V5LmNvbYIJAK+ii7kzrdqsMAwGA1Ud ++EwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEARpb86RP/ck55X+NunXeIX81i763b ++j7Z1VJwFbA/QfupzxnqJ2IP/lxC8YxJ3Bp2IJMI7rC9r0poa41ZxI5rGHip97Dpg ++sxPF9lkRUmKBBQjkICOq1w/4d2DRInBoqXttD+0WsqDfNDVK+7kSE07ytn3RzHCj ++j0gv0PdxmuCsR/E= ++ ++MIIDzzCCAzigAwIBAgIJAK+ii7kzrdqtMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD ++VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1MIFNlY3Vy ++aXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2VjKTEQMA4G ++A1UECxMHUm9vdCBDQTEWMBQGA1UEAxMNQWxla3NleSBTYW5pbjEhMB8GCSqGSIb3 ++DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tMCAXDTE0MDUyMzE3NTIzOFoYDzIxMTQw ++NDI5MTc1MjM4WjCBnDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWEx ++PTA7BgNVBAoTNFhNTCBTZWN1cml0eSBMaWJyYXJ5IChodHRwOi8vd3d3LmFsZWtz ++ZXkuY29tL3htbHNlYykxFjAUBgNVBAMTDUFsZWtzZXkgU2FuaW4xITAfBgkqhkiG ++9w0BCQEWEnhtbHNlY0BhbGVrc2V5LmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgC ++QQCyuvKJ2CuUPD33ghPt4Q8MilesHxVbbpyKfmabrYVpDGVDmOKKp337qJUZZ95K ++fwlXbR2j0zyKWJmvRxUx+PsTAgMBAAGjggFFMIIBQTAMBgNVHRMEBTADAQH/MCwG ++CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV ++HQ4EFgQU/uTsUyTwlZXHELXhRLVdOWVa434wgeMGA1UdIwSB2zCB2IAUBrWkrKeq ++dUTqFZxP3wWDT2oe/guhgbSkgbEwga4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpD ++YWxpZm9ybmlhMT0wOwYDVQQKEzRYTUwgU2VjdXJpdHkgTGlicmFyeSAoaHR0cDov ++L3d3dy5hbGVrc2V5LmNvbS94bWxzZWMpMRAwDgYDVQQLEwdSb290IENBMRYwFAYD ++VQQDEw1BbGVrc2V5IFNhbmluMSEwHwYJKoZIhvcNAQkBFhJ4bWxzZWNAYWxla3Nl ++eS5jb22CCQCvoou5M63arDANBgkqhkiG9w0BAQUFAAOBgQBuTAW63AgWqqUDPGi8 ++BiXbdKHhFP4J8qgkdv5WMa6SpSWVgNgOYXkK/BSg1aSmQtGv8/8UvBRPoJnO4y0N ++jWUFf1ubOgUNmedYNLq7YbTp8yTGWeogCyM2xdWELMP8BMgQL0sP+MDAFMKO3itY ++mEWnCEsP15HKSTms54RNj7oJ+A== ++MIIDfzCCAymgAwIBAgIJAK+ii7kzrdq1MA0GCSqGSIb3DQEBBQUAMIGcMQswCQYD ++VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1MIFNlY3Vy ++aXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2VjKTEWMBQG ++A1UEAxMNQWxla3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtz ++ZXkuY29tMCAXDTE3MDIxNTIyMDgyMVoYDzIxMTcwMTIyMjIwODIxWjCBojELMAkG ++A1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExPTA7BgNVBAoTNFhNTCBTZWN1 ++cml0eSBMaWJyYXJ5IChodHRwOi8vd3d3LmFsZWtzZXkuY29tL3htbHNlYykxHDAa ++BgNVBAMTE0VDRFNBIHNlY3AyNTZyMSBLZXkxITAfBgkqhkiG9w0BCQEWEnhtbHNl ++Y0BhbGVrc2V5LmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABG0uzqp1mHfR ++9zEMsYiIXkNPttZyj0aP8iE9wlKNp9x73kJnNmll8l47MZDWVjtrcyFn1XUzxH7q ++WVJYjPBGptSjggFFMIIBQTAMBgNVHRMEBTADAQH/MCwGCWCGSAGG+EIBDQQfFh1P ++cGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUeiw8r3+wiHw0 ++5T+3qRhjfhAZu9kwgeMGA1UdIwSB2zCB2IAU/uTsUyTwlZXHELXhRLVdOWVa436h ++gbSkgbEwga4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMT0wOwYD ++VQQKEzRYTUwgU2VjdXJpdHkgTGlicmFyeSAoaHR0cDovL3d3dy5hbGVrc2V5LmNv ++bS94bWxzZWMpMRAwDgYDVQQLEwdSb290IENBMRYwFAYDVQQDEw1BbGVrc2V5IFNh ++bmluMSEwHwYJKoZIhvcNAQkBFhJ4bWxzZWNAYWxla3NleS5jb22CCQCvoou5M63a ++rTANBgkqhkiG9w0BAQUFAANBABs4Ie691BR30Ki9MM5jzGhWlnWs8SaGJkAlJxck ++hhVmvV/5cntLvuQNGPpZJtkY8cVIw2/G4XmBBad7lZcuOag= ++ ++ some text ++ +diff --git a/tests/testDSig.sh b/tests/testDSig.sh +index 3d653346..863b1d2f 100755 +--- a/tests/testDSig.sh ++++ b/tests/testDSig.sh +@@ -1119,6 +1119,14 @@ execDSigTest $res_success \ + "ec x509" \ + "--trusted-$cert_format $topfolder/keys/enveloped-ecdsa-java-bug-cert.$cert_format --enabled-key-data x509 --verification-gmt-time 2019-01-01+00:00:00" + ++# see issue https://github.com/lsh123/xmlsec/issues/941 (another java bug) ++execDSigTest $res_success \ ++ "" \ ++ "aleksey-xmldsig-01/enveloping-sha256-ecdsa-sha256_padded" \ ++ "sha256 ecdsa-sha256" \ ++ "ec x509" \ ++ "--insecure --enabled-key-data x509" ++ + execDSigTest $res_success \ + "" \ + "aleksey-xmldsig-01/x509data-test" \ +-- +2.46.0 + diff --git a/vcpkg.json b/vcpkg.json index a57ca3ff2..01223ffad 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -15,7 +15,7 @@ "features": { "tests": { "description": "Build tests", "dependencies": ["boost-test"] } }, - "builtin-baseline": "031ad89ce6c575df35a8e58707ad2c898446c63e", + "builtin-baseline": "98e7cd3a7ba579efc543f8854af800d033031eae", "vcpkg-configuration": { "overlay-triplets": ["./vcpkg-triplets"], "overlay-ports": ["vcpkg-ports/xmlsec"]