Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion examples/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
}

Expand Down
2 changes: 0 additions & 2 deletions examples/ios/libdigidocpp/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeIconFiles</key>
<array/>
<key>CFBundleTypeName</key>
<string>DigiDoc signed document</string>
<key>CFBundleTypeRole</key>
Expand Down
6 changes: 1 addition & 5 deletions libdigidocpp.wxs
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,6 @@
<?endif?>
</ComponentGroup>

<ComponentGroup Id="DependenciesDev" Source="$(var.vcpkg)\debug\bin">
<File Name="zlibd1.dll" />
</ComponentGroup>

<ComponentGroup Id="LibrariesDev" Source="$(var.libdigidocpp)">
<Files Include="*.pdb" />
<File Name="digidocpp.lib" />
Expand All @@ -105,7 +101,7 @@
<Files Directory="INSTALLFOLDER" Subdirectory="schema" Include="$(var.libdigidocpp)\etc\digidocpp\schema\*.xsd" />
<Feature Id="InstallDevel" Level="1" Title="Development">
<ComponentGroupRef Id="RuntimeDev" />
<ComponentGroupRef Id="DependenciesDev" />
<File Directory="INSTALLFOLDER" Source="$(var.vcpkg)\debug\bin\zlibd1.dll" />
<ComponentGroupRef Id="LibrariesDev" />
<Files Directory="INSTALLFOLDER" Subdirectory="include" Include="$(var.libdigidocpp)\include\**" />
<Files Directory="INSTALLFOLDER" Subdirectory="cmake\libdigidocpp" Include="$(var.libdigidocpp)\cmake\libdigidocpp\**" />
Expand Down
3 changes: 2 additions & 1 deletion prepare_osx_build_environment.sh
Original file line number Diff line number Diff line change
@@ -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="$@"

Expand Down Expand Up @@ -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" ;;
Expand Down
35 changes: 22 additions & 13 deletions src/XMLDocument.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <xmlsec/xmltree.h>
#include <xmlsec/xmldsig.h>
#include <xmlsec/crypto.h>
#include <xmlsec/parser.h>

#include <openssl/evp.h>

Expand All @@ -45,7 +46,7 @@ static std::vector<unsigned char> from_base64(std::string_view data)
std::vector<unsigned char> 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_free>(EVP_ENCODE_CTX_new());
EVP_DecodeInit(ctx.get());

for(auto pos = data.find_first_of(whitespace);
Expand All @@ -71,7 +72,7 @@ static std::vector<unsigned char> from_base64(std::string_view data)
static std::string to_base64(const std::vector<unsigned char> &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_free>(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)
Expand Down Expand Up @@ -305,19 +306,27 @@ struct XMLDocument: public unique_free_t<xmlDoc>, 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<xmlFreeParserCtxt>(xmlCreateIOParserCtxt(nullptr, nullptr, [](void *context, char *buffer, int len) -> int {
auto *is = static_cast<std::istream *>(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};
}

Expand Down Expand Up @@ -358,11 +367,11 @@ struct XMLDocument: public unique_free_t<xmlDoc>, 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<xmlOutputBufferClose>(xmlOutputBufferCreateIO([](void *context, const char *buffer, int len) {
auto *digest = static_cast<Digest *>(context);
digest->update(pcxmlChar(buffer), size_t(len));
return len;
}, nullptr, const_cast<Digest*>(&digest), nullptr), xmlOutputBufferClose);
}, nullptr, const_cast<Digest*>(&digest), nullptr));
int size = xmlC14NExecute(get(), [](void *root, xmlNodePtr node, xmlNodePtr parent) constexpr noexcept {
if(root == node)
return 1;
Expand Down Expand Up @@ -396,14 +405,14 @@ struct XMLDocument: public unique_free_t<xmlDoc>, public XMLNode

void validateSchema(const std::string &schemaPath) const
{
auto parser = make_unique_ptr(xmlSchemaNewParserCtxt(schemaPath.c_str()), xmlSchemaFreeParserCtxt);
auto parser = make_unique_ptr<xmlSchemaFreeParserCtxt>(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<xmlSchemaFree>(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<xmlSchemaFreeValidCtxt>(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"));
Expand All @@ -414,12 +423,12 @@ struct XMLDocument: public unique_free_t<xmlDoc>, public XMLNode

static bool verifySignature(XMLNode signature, [[maybe_unused]] Exception *e = {}) noexcept
{
auto mngr = make_unique_ptr(xmlSecKeysMngrCreate(), xmlSecKeysMngrDestroy);
auto mngr = make_unique_ptr<xmlSecKeysMngrDestroy>(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<xmlSecDSigCtxDestroy>(xmlSecDSigCtxCreate(mngr.get()));
if(!ctx)
return false;
ctx->keyInfoReadCtx.flags |= XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS;
Expand Down
60 changes: 27 additions & 33 deletions src/crypto/X509Cert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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;
Expand Down Expand Up @@ -297,8 +300,7 @@ string X509Cert::serial() const
return {};
if(auto bn = make_unique_ptr<BN_free>(ASN1_INTEGER_to_BN(X509_get_serialNumber(cert.get()), nullptr)))
{
auto openssl_free = [](char *data) { OPENSSL_free(data); };
if(auto str = unique_ptr<char,decltype(openssl_free)>(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 {};
Expand All @@ -316,15 +318,19 @@ string X509Cert::issuerName(const string &obj) const
return toString<X509_get_issuer_name>(obj);
}

template<auto Func>
constexpr auto X509Cert::extension(int nid) const noexcept
{
return make_unique_cast<Func>(cert ? X509_get_ext_d2i(cert.get(), nid, nullptr, nullptr) : nullptr);
}

/**
* Returns current certificate key usage bits
*/
vector<X509Cert::KeyUsage> X509Cert::keyUsage() const
{
vector<KeyUsage> usage;
if(!cert)
return usage;
auto keyusage = make_unique_cast<ASN1_BIT_STRING_free>(X509_get_ext_d2i(cert.get(), NID_key_usage, nullptr, nullptr));
auto keyusage = extension<ASN1_BIT_STRING_free>(NID_key_usage);
if(!keyusage)
return usage;

Expand All @@ -342,9 +348,7 @@ vector<X509Cert::KeyUsage> X509Cert::keyUsage() const
vector<string> X509Cert::certificatePolicies() const
{
vector<string> pol;
if(!cert)
return pol;
auto cp = make_unique_cast<CERTIFICATEPOLICIES_free>(X509_get_ext_d2i(cert.get(), NID_certificate_policies, nullptr, nullptr));
auto cp = extension<CERTIFICATEPOLICIES_free>(NID_certificate_policies);
if(!cp)
return pol;
for(int i = 0; i < sk_POLICYINFO_num(cp.get()); ++i)
Expand Down Expand Up @@ -375,30 +379,22 @@ vector<string> X509Cert::qcStatements() const
if(oid == QC_SYNTAX2)
{
#ifndef TEMPLATE
if(!s->statementInfo)
continue;
auto si = make_unique_cast<SemanticsInformation_free>(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<SemanticsInformation_free>(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
}
else if(oid == QC_QCT)
{
#ifndef TEMPLATE
if(!s->statementInfo)
continue;
auto qct = make_unique_cast<QcType_free>(ASN1_item_unpack(s->statementInfo->value.sequence, ASN1_ITEM_rptr(QcType)));
auto qct = make_unique_cast<QcType_free>(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));
Expand Down Expand Up @@ -487,9 +483,7 @@ X509* X509Cert::handle() const
*/
bool X509Cert::isCA() const
{
if(!cert)
return false;
auto cons = make_unique_cast<BASIC_CONSTRAINTS_free>(X509_get_ext_d2i(cert.get(), NID_basic_constraints, nullptr, nullptr));
auto cons = extension<BASIC_CONSTRAINTS_free>(NID_basic_constraints);
return cons && cons->ca > 0;
}

Expand Down Expand Up @@ -532,19 +526,19 @@ 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;
}

/**
* Equal operator to compare two objects
*/
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());
}

/**
Expand All @@ -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)

Expand Down
2 changes: 2 additions & 0 deletions src/crypto/X509Cert.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ namespace digidoc
static std::string toOID(ASN1_OBJECT *obj);
template<auto Func>
std::string toString(const std::string &obj) const;
template<auto Func>
constexpr auto extension(int nid) const noexcept;
std::shared_ptr<X509> cert;
};
}
1 change: 1 addition & 0 deletions vcpkg-ports/xmlsec/portfile.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
Expand Down
1 change: 1 addition & 0 deletions vcpkg-ports/xmlsec/vcpkg.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
Loading
Loading