Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
1b76e52
Added CDocV1 tests
Jan 9, 2026
7a2b181
Added CDoc1 tests and cleaned up testing
Jan 9, 2026
2629756
More CDocCipher cleanups
Jan 13, 2026
4febb5c
Fix windows build
Jan 13, 2026
dd2cf68
Fix initializer order for gcc
Jan 13, 2026
5e4e80f
Use again absolute paths for tests
Jan 13, 2026
bb66be5
Create test tmp path if not exist
Jan 13, 2026
a4efe25
Change SID/MID callback signature to handle QR codes
Jan 14, 2026
0cab1cb
Apply suggestions from code review
lauris71 Jan 14, 2026
0da1fd8
Update cdoc/cdoc-tool.cpp
lauris71 Jan 14, 2026
75d1101
Update cdoc/cdoc-tool.cpp
lauris71 Jan 14, 2026
5b32d9f
Merge branch 'open-eid:master' into master
lauris71 Jan 14, 2026
87e5493
Merge branch 'master' into sid2
Jan 14, 2026
53a6898
Some fixes from ai security audit
May 20, 2026
47715fe
Fixed cdoc-tool index usage
May 22, 2026
fef2389
Update cdoc/cdoc-tool.cpp
lauris71 May 25, 2026
8848542
Update cdoc/cdoc-tool.cpp
lauris71 May 25, 2026
bf5ca62
Some more AI issue fixes
May 26, 2026
0a6b12b
Hardcode SSL timeout
May 27, 2026
a4dafb9
Fixed xstream uint overflow and tool logging
May 27, 2026
8c9b789
Some more fixes
May 27, 2026
e9a8a6e
Secure tool key handling, use explicid compile time definitions for k…
May 27, 2026
fa288ee
Fixed potential tar size overflow and secured proxy password
May 27, 2026
b89e4f1
Windows build fix
May 27, 2026
d0e0c90
Revert proxy password for now
May 27, 2026
87d1547
Make proxy password string_view
May 29, 2026
3631991
Disable potential Bleichenbacher attack for CDoc1 RSA encryption
Jun 1, 2026
7e6cdaa
Moved fix to main decryptRSA method
Jun 1, 2026
f01510a
Added ct.h
Jun 1, 2026
0aec4a1
Bleichenbacher fix for NCrypt backend
Jun 1, 2026
6f37761
Some cleanups
Jun 1, 2026
cc19133
All C,H & M fixes from Caludo Opus review
Jun 5, 2026
6bd12a8
Merge branch 'open-eid:master' into master
lauris71 Jun 5, 2026
8102224
Update cdoc-tool
Jun 5, 2026
c3d8dd2
Merge branch 'open-eid:master' into master
lauris71 Jun 11, 2026
bb9945c
Merge branch 'master' into ai-security
Jun 11, 2026
08ca9e5
Make default KDF iter 600000
Jun 11, 2026
004ce57
Added std_string_view.i
Jun 16, 2026
212825a
Fixed label parsing on Ubuntu 22
Jun 16, 2026
6db3372
Include <string.h>
Jun 17, 2026
8a2f494
Use explicit_bzero on glibc
Jun 17, 2026
573f147
Use SecureZeroMemory on windows
Jun 17, 2026
2d552cb
Use OPENSSL_cleanse for secure cleanup
Jun 17, 2026
f63ed77
Fixed inverted constant-time comparison
Jun 17, 2026
a6fc79a
Merge branch 'open-eid:master' into master
lauris71 Jun 17, 2026
88f1fa7
Merge commit 'a6fc79ad2e3c4d9d34157742f84db1c0c25faca3' into ai-security
Jun 17, 2026
578ff3a
Merge branch 'ai-security' into sid2
Jul 2, 2026
c0626a0
Update cdoc/CDocCipher.cpp
metsma Jul 2, 2026
eb091c3
Fix build for SIDMID
Jul 2, 2026
0e544fc
Fixed PKCS11 and NCrypt RSA handling and check all random() calls for…
Jul 2, 2026
3641679
Fix share encryption
Jul 3, 2026
3e62abe
Fixed PKCS11 public key loading
Jul 3, 2026
68cc522
Merge branch 'open-eid:master' into master
lauris71 Jul 3, 2026
32995f7
Merge branch 'master' into sid2
Jul 3, 2026
ed72221
fixes
Jul 15, 2026
5769447
Fixed label UTF-8 escaping if locale is not C
Jul 15, 2026
a7b7c6b
Merge branch 'open-eid:master' into master
lauris71 Jul 15, 2026
1225355
Fixed label generation for non-C locales
Jul 15, 2026
4ba85f1
Merge branch 'master' of github.com:lauris71/libcdoc
Jul 15, 2026
e5ac2a3
Merge branch 'open-eid:master' into master
lauris71 Jul 24, 2026
1df498c
Working authentication
Jul 24, 2026
463bfd5
Merge branch 'master' into ai-security
Jul 24, 2026
dc23ffa
Added SecureTarget class
Jul 24, 2026
139d1a7
Fixed N1 (out-of-bounds read) from 2026-07 report
Jul 27, 2026
e37902c
Added Utils.cpp to test build
Jul 27, 2026
e7ad099
Delete reader and writer in TestPasswordWithlabel
Jul 27, 2026
5743645
Clean up z_stream on zlib errors
Jul 27, 2026
6ae5ab9
Fixed N3 (base64 decode throws on errors) from 202607 report
Jul 27, 2026
90c72e6
Initial working SID decryption
Aug 4, 2026
31fb917
Preliminary configuration settings for MID
Aug 4, 2026
eaf6bff
Check that we have session disclosures for all share servers before c…
Aug 5, 2026
a63652e
Added CDoc2Reader.cpp changes
Aug 5, 2026
976cab5
Merge branch 'ai-security' into sid2
Aug 5, 2026
9b65423
More fixes to SID
Aug 5, 2026
adc94f4
Validate auth and rp server signatures
Aug 6, 2026
df0a4ed
Preliminary working MobileId authentication
Aug 7, 2026
b638e6d
Added MID signature and certificate validation
Aug 7, 2026
36545e6
Fixed keyshare KEK creatin, urlencode untrusted path components, log …
Aug 12, 2026
8492680
Verify shares recipient and vc code
Aug 12, 2026
9b01b40
Made SessionData public
Aug 14, 2026
08b5d44
Added tests for MID/SID (not automatic)
Aug 14, 2026
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
14 changes: 14 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,20 @@ jobs:
with:
name: ${{ matrix.target }}
path: ${{ env.DEST }}
sanitizers:
name: Run unit tests with ASan+UBSan
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Install dependencies
run: sudo apt update -qq && sudo apt install --no-install-recommends -y ninja-build libboost-test-dev ${UBUNTU_DEPS}
- name: Configure
run: cmake --preset sanitize
- name: Build
run: cmake --build --preset sanitize --target unittests
- name: Test
run: ctest --test-dir build/sanitize --output-on-failure
windows:
name: Build on Windows
runs-on: ${{ matrix.image }}
Expand Down
21 changes: 20 additions & 1 deletion CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"rhs": "Darwin"
},
"cacheVariables": {
"CMAKE_OSX_ARCHITECTURES": "arm64;x86_64",
"CMAKE_OSX_ARCHITECTURES": "arm64",
"CMAKE_OSX_DEPLOYMENT_TARGET": "14.0",
"CMAKE_FIND_ROOT_PATH": "$env{DEST};/opt/homebrew",
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
Expand Down Expand Up @@ -124,10 +124,29 @@
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
"VCPKG_TARGET_TRIPLET": "$env{PLATFORM}-windows-static-md"
}
},
{
"name": "sanitize",
"displayName": "Sanitizers (ASan+UBSan)",
"description": "Debug build with AddressSanitizer and UndefinedBehaviorSanitizer for running the unit tests (system dependencies, no vcpkg; requires boost-test, openssl, libxml2, zlib and flatbuffers development packages)",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_CXX_FLAGS": "-fsanitize=address,undefined -fno-omit-frame-pointer",
"CMAKE_EXE_LINKER_FLAGS": "-fsanitize=address,undefined",
"CMAKE_SHARED_LINKER_FLAGS": "-fsanitize=address,undefined",
"CMAKE_DISABLE_FIND_PACKAGE_SWIG": "YES",
"CMAKE_DISABLE_FIND_PACKAGE_Doxygen": "YES"
}
}
],
"buildPresets":
[
{
"name": "sanitize",
"configurePreset": "sanitize"
},
{
"name": "macos",
"configurePreset": "macos"
Expand Down
7 changes: 3 additions & 4 deletions cdoc/CDoc1Reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,22 +164,21 @@ CDoc1Reader::getFMK(std::vector<uint8_t>& fmk, unsigned int lock_idx)
// The downstream AES decrypt at the body level is what tells
// success from failure.
} else {
std::vector<uint8_t> key;
int result = crypto->deriveConcatKDF(key,
SecureTarget key;
int result = crypto->deriveConcatKDF(key.getTarget(),
lock.getBytes(Lock::Params::KEY_MATERIAL),
lock.getString(Lock::Params::CONCAT_DIGEST),
lock.getBytes(Lock::Params::ALGORITHM_ID),
lock.getBytes(Lock::Params::PARTY_UINFO),
lock.getBytes(Lock::Params::PARTY_VINFO),
lock_idx);
if (result < 0) {
libcdoc::cleanse(key);
setLastError(FAIL_MSG);
LOG_ERROR("{}", last_error);
return libcdoc::CRYPTO_ERROR;
}
fmk = libcdoc::Crypto::AESWrap(key, lock.encrypted_fmk, false);
libcdoc::cleanse(key);
key.cleanse();
// AESWrap returns {} on failure. Pad the candidate to expected
// length so the failure shape matches the RSA path; the bytes
// are arbitrary because the body decrypt is going to reject
Expand Down
207 changes: 144 additions & 63 deletions cdoc/CDoc2Reader.cpp

Large diffs are not rendered by default.

24 changes: 19 additions & 5 deletions cdoc/CDoc2Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,8 +346,11 @@ CDoc2Writer::buildHeader(std::vector<uint8_t>& header, const std::vector<libcdoc
FAIL("Missing server list for ID " + rcpt.server_id, libcdoc::CONFIGURATION_ERROR);
LOG_DBG("Share servers: {}", url_list);
std::vector<std::string> urls = libcdoc::JsonToStringArray(url_list);
if (urls.size() < 1)
FAIL("No server URLs in " + rcpt.server_id, libcdoc::CONFIGURATION_ERROR);
// S5: with fewer than 2 servers the XOR "split" would hand the
// complete KEK to a single server, defeating the threshold
// protection - refuse to produce such a container.
if (urls.size() < 2)
FAIL("At least 2 share server URLs are required for ID " + rcpt.server_id, libcdoc::CONFIGURATION_ERROR);
int N_SHARES = urls.size();
LOG_DBG("Number of shares: {}", N_SHARES);

Expand All @@ -372,8 +375,11 @@ CDoc2Writer::buildHeader(std::vector<uint8_t>& header, const std::vector<libcdoc
// key_material is split-share-input material; wipe on exit.
libcdoc::Cleanser key_material_guard(key_material);

//KEK_i_pm = HKDF_Extract(KeyMaterialSalt_i, KeyMaterial_i)
std::vector<uint8_t> kek_pm = libcdoc::Crypto::extract(key_material_salt, key_material);
// KEK_i_pm = HKDF_Extract(KeyMaterialSalt_i, KeyMaterial_i)
// RFC 5869: HKDF-Extract(salt, IKM); Crypto::extract takes (IKM, salt).
// (S11: the arguments were swapped, deviating from the spec and
// the reference implementation.)
std::vector<uint8_t> kek_pm = libcdoc::Crypto::extract(key_material, key_material_salt);
libcdoc::Cleanser kek_pm_guard(kek_pm);

// KEK_i = HKDF_Expand(KEK_i_pm, "CDOC2kek" + FMKEncryptionMethod + RecipientInfo_i, L)
Expand Down Expand Up @@ -419,7 +425,7 @@ CDoc2Writer::buildHeader(std::vector<uint8_t>& header, const std::vector<libcdoc
std::vector<std::vector<uint8_t>> transaction_ids(N_SHARES);
for (int i = 0; i < N_SHARES; i++) {
std::string send_url = urls[i];
LOG_TRACE_KEY("Sending share: {} {} {}", i, send_url, libcdoc::toHex(kek_shares[i]));
LOG_TRACE("Sending share: {} {} {}", i, send_url, libcdoc::toHex(kek_shares[i]));
int result = network->sendShare(transaction_ids[i], send_url, RecipientInfo_i, kek_shares[i]);
if (result < 0)
FAIL(network->getLastErrorStr(result), result);
Expand Down Expand Up @@ -479,6 +485,14 @@ CDoc2Writer::addRecipient(const libcdoc::Recipient& rcpt)
if(!rcpt.validate())
FAIL("Invalid recipient parameters", libcdoc::WRONG_ARGUMENTS);
break;
#ifdef HAS_KEYSHARES
case Recipient::KEYSHARE:
if (!network)
FAIL("KeyShares require NetworkBackend", libcdoc::WORKFLOW_ERROR);
if (!rcpt.validate())
FAIL("Invalid recipient parameters", libcdoc::WRONG_ARGUMENTS);
break;
#endif
default:
FAIL("Invalid recipient type", WRONG_ARGUMENTS);
}
Expand Down
6 changes: 6 additions & 0 deletions cdoc/CDocCipher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,12 @@ fill_recipients_from_rcpt_info(ToolConf& conf, ToolCrypto& crypto, std::vector<l
key.setLabelValue(CDoc2::Label::LABEL, rcpt.label);
#ifdef HAS_KEYSHARES
} else if (rcpt.type == RcptInfo::Type::SHARE) {
if (conf.servers.empty()) {
// S4: a share recipient without --server indexed servers[0]
// out of bounds.
LOG_ERROR("Keyshare recipient '{}' requires a share server (--server ID URL)", rcpt.label);
continue;
}
LOG_DBG("Creating keyshare recipient:");
key = libcdoc::Recipient::makeShare(std::move(label), conf.servers[0].ID, "PNOEE-" + rcpt.id);
#endif
Expand Down
11 changes: 10 additions & 1 deletion cdoc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ add_library(cdoc
CDoc2Writer.cpp CDoc2Writer.h
DDocReader.cpp DDocReader.h
DDocWriter.cpp DDocWriter.h
KeyShares.cpp KeyShares.h
# KeyShares.cpp KeyShares.h
XmlReader.cpp XmlReader.h
XmlWriter.cpp XmlWriter.h
Expand Down Expand Up @@ -79,6 +80,9 @@ target_include_directories(cdoc PUBLIC
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
)

# Enable SID/MID
target_compile_definitions(cdoc PRIVATE HAS_KEYSHARES)

if(NOT BUILD_SHARED_LIBS)
target_compile_definitions(cdoc PUBLIC cdoc_STATIC)
endif()
Expand All @@ -102,6 +106,10 @@ target_link_libraries(cdoc PRIVATE
if(BUILD_TOOLS)
add_executable(cdoc-tool cdoc-tool.cpp)
target_include_directories(cdoc-tool PRIVATE ${OPENSSL_INCLUDE_DIR})

# Enable SID/MID
target_compile_definitions(cdoc-tool PRIVATE HAS_KEYSHARES)

target_link_libraries(cdoc-tool cdoc_ver cdoc OpenSSL::SSL)
target_link_options(cdoc-tool PRIVATE
$<$<PLATFORM_ID:Windows>: /MANIFEST:NO /MANIFEST:EMBED /MANIFESTINPUT:${CMAKE_CURRENT_SOURCE_DIR}/cdoc-tool.manifest>
Expand Down Expand Up @@ -152,8 +160,9 @@ if(SWIG_FOUND)
set_target_properties(cdoc_java PROPERTIES
INSTALL_RPATH $<$<PLATFORM_ID:Darwin>:/Library/Frameworks>
SWIG_COMPILE_DEFINITIONS $<$<PLATFORM_ID:Windows>:SWIGWIN>
SWIG_COMPILE_DEFINITIONS HAS_KEYSHARES
)
#install(TARGETS cdoc_java DESTINATION $<IF:$<PLATFORM_ID:Darwin>,/Library/Java/Extensions,${CMAKE_INSTALL_LIBDIR}>) # FIXME: build mac packages
target_compile_definitions(cdoc_java PRIVATE HAS_KEYSHARES)
install(TARGETS cdoc_java DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/java/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ee/ria/cdoc FILES_MATCHING PATTERN "*.java")
if(WIN32)
Expand Down
32 changes: 11 additions & 21 deletions cdoc/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ struct CDOC_EXPORT Configuration {
* @brief Fetch URL of keyserver (Domain is server id)
*/
static constexpr char const *KEYSERVER_FETCH_URL = "KEYSERVER_FETCH_URL";
/**
* @brief Authentication session server for SID/MID
*/
static constexpr char const *AUTH_SERVER = "AUTH_SERVER";
/**
* @brief RP server for SID/MID
*/
static constexpr char const *RP_SERVER = "RP_SERVER";
#ifdef HAS_KEYSHARES
/**
* @brief JSON array of share server base urls (Domain is server id)
Expand All @@ -51,28 +59,10 @@ struct CDOC_EXPORT Configuration {
* @brief Method for signing keyshare tickets (SMART_ID or MOBILE_ID)
*/
static constexpr char const *SHARE_SIGNER = "SHARE_SIGNER";
static constexpr char const *SHARE_SIGNER_SID = "SMART_ID";
static constexpr char const *SHARE_SIGNER_MID = "MOBILE_ID";
/**
* @brief Domain of SmartID settings
*/
static constexpr char const *SID_DOMAIN = "SMART_ID";
/**
* @brief Domain of Mobile ID settings
*/
static constexpr char const *MID_DOMAIN = "MOBILE_ID";
/**
* @brief MID/SID base url (domain is SMART_ID or MOBILE_ID)
*/
static constexpr char const *BASE_URL = "BASE_URL";
/**
* @brief MID/SID relying party UUID (domain is SMART_ID or MOBILE_ID)
*/
static constexpr char const *RP_UUID = "RP_UUID";
/**
* @brief MID/SID relying party name (domain is SMART_ID or MOBILE_ID)
*/
static constexpr char const *RP_NAME = "RP_NAME";
/**
* @brief Mobile ID phone number (domain is MOBILE_ID)
* @brief Mobile ID phone number
*/
static constexpr char const *PHONE_NUMBER = "PHONE_NUMBER";
#endif
Expand Down
126 changes: 124 additions & 2 deletions cdoc/Crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
#include <openssl/aes.h>
#include <openssl/err.h>
#include <openssl/hmac.h>
#include <openssl/ecdsa.h>
#include <openssl/kdf.h>
#include <openssl/param_build.h>
#include <openssl/opensslv.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
Expand Down Expand Up @@ -59,6 +61,119 @@ const std::string Crypto::RSA_MTH = "http://www.w3.org/2001/04/xmlenc#rsa-1_5";
const std::string Crypto::CONCATKDF_MTH = "http://www.w3.org/2009/xmlenc11#ConcatKDF";
const std::string Crypto::AGREEMENT_MTH = "http://www.w3.org/2009/xmlenc11#ECDH-ES";

// Convert a raw ECDSA r||s signature (JWS/RFC9421 convention) to the DER
// SEQUENCE-of-INTEGERs form expected by OpenSSL.
static std::vector<uint8_t>
ecRawSigToDer(const std::vector<uint8_t> &signature)
{
if (signature.empty() || signature.size() % 2 != 0)
return {};
size_t half = signature.size() / 2;
auto sig = make_unique_ptr<ECDSA_SIG_free>(ECDSA_SIG_new());
if (!sig)
return {};
if (ECDSA_SIG_set0(sig.get(),
BN_bin2bn(signature.data(), int(half), nullptr),
BN_bin2bn(signature.data() + half, int(half), nullptr)) != 1)
return {};
int len = i2d_ECDSA_SIG(sig.get(), nullptr);
if (len <= 0)
return {};
auto der = std::vector<uint8_t>(static_cast<size_t>(len));
uint8_t *out = der.data();
if (i2d_ECDSA_SIG(sig.get(), &out) != len)
return {};
return der;
}

bool
Crypto::validateSignature(const std::vector<uint8_t> &cert_der,
const std::vector<uint8_t> &data,
const std::vector<uint8_t> &signature,
SignatureAlgorithm algo)
{
const unsigned char *ptr = cert_der.data();
auto x509 = make_unique_ptr<X509_free>(d2i_X509(nullptr, &ptr, long(cert_der.size())));
if (!x509)
return false;
auto pkey = make_unique_ptr<EVP_PKEY_free>(X509_get_pubkey(x509.get()));
if (!pkey)
return false;
auto ctx = make_unique_ptr<EVP_PKEY_CTX_free>(EVP_PKEY_CTX_new(pkey.get(), nullptr));
if (!ctx)
return false;
switch (algo) {
case SignatureAlgorithm::RSASSA_PSS_SHA256: {
// The provider's one-shot EVP_PKEY_verify for RSA requires the
// input to be the message digest already, so hash `data` first.
uint8_t md_value[EVP_MAX_MD_SIZE];
unsigned int md_len = 0;
if (EVP_Digest(data.data(), data.size(), md_value, &md_len, EVP_sha256(), nullptr) != 1)
return false;
if (EVP_PKEY_verify_init(ctx.get()) != 1)
return false;
if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_PSS_PADDING) <= 0 ||
EVP_PKEY_CTX_set_signature_md(ctx.get(), EVP_sha256()) <= 0 ||
EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), EVP_sha256()) <= 0 ||
EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx.get(), RSA_PSS_SALTLEN_DIGEST) <= 0)
return false;
return EVP_PKEY_verify(ctx.get(), signature.data(), signature.size(), md_value, md_len) == 1;
}
case SignatureAlgorithm::ES256: {
// ECDSA verifies the given digest directly; the signature arrives as
// raw r||s (JWS convention) and must be re-wrapped into DER.
if (data.size() != 32)
return false;
auto der = ecRawSigToDer(signature);
if (der.empty())
return false;
if (EVP_PKEY_verify_init(ctx.get()) != 1)
return false;
if (EVP_PKEY_CTX_set_signature_md(ctx.get(), EVP_sha256()) <= 0)
return false;
return EVP_PKEY_verify(ctx.get(), der.data(), der.size(), data.data(), data.size()) == 1;
}
}
return false;
}

bool
Crypto::validateSignatureECPoint(const std::vector<uint8_t> &pubkey_point,
const std::vector<uint8_t> &digest,
const std::vector<uint8_t> &signature)
{
if (pubkey_point.size() != 65 || pubkey_point[0] != 0x04 || digest.size() != 32)
return false;
auto ctx = make_unique_ptr<EVP_PKEY_CTX_free>(
EVP_PKEY_CTX_new_from_name(nullptr, "EC", nullptr));
if (!ctx)
return false;
// The group name string must outlive EVP_PKEY_fromdata (it is referenced,
// not copied)
char group_name[] = "P-256";
OSSL_PARAM params[] = {
OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, group_name, 0),
OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY, (void *) pubkey_point.data(), pubkey_point.size()),
OSSL_PARAM_construct_end()
};
EVP_PKEY *raw_pkey = nullptr;
if (EVP_PKEY_fromdata_init(ctx.get()) != 1 ||
EVP_PKEY_fromdata(ctx.get(), &raw_pkey, EVP_PKEY_PUBLIC_KEY, params) != 1)
return false;
auto pkey = make_unique_ptr<EVP_PKEY_free>(raw_pkey);
auto vctx = make_unique_ptr<EVP_PKEY_CTX_free>(EVP_PKEY_CTX_new(pkey.get(), nullptr));
if (!vctx)
return false;
auto der = ecRawSigToDer(signature);
if (der.empty())
return false;
if (EVP_PKEY_verify_init(vctx.get()) != 1)
return false;
if (EVP_PKEY_CTX_set_signature_md(vctx.get(), EVP_sha256()) <= 0)
return false;
return EVP_PKEY_verify(vctx.get(), der.data(), der.size(), digest.data(), digest.size()) == 1;
}

std::vector<uint8_t> Crypto::AESWrap(const std::vector<uint8_t> &key, const std::vector<uint8_t> &data, bool encrypt)
{
// Note: AES_set_{encrypt,decrypt}_key return 0 on success and a negative
Expand Down Expand Up @@ -628,8 +743,15 @@ void unpadPKCS1v15CT(const std::vector<uint8_t> &em,
// range since em.size() >= 11+expected_len > 0). The clamped value
// is replaced by synth[i] below when good == 0, so the actual
// bytes read here never reach the caller.
size_t in_range = size_t(ge_size(em.size() - 1, src_idx)); // 0 or 0xFF
size_t mask = in_range & ~size_t(0);
// ge_size() returns a single-byte mask (0x00 or 0xFF). It must be
// widened to a full-width size_t mask before splicing indices;
// using the byte mask directly would mix the low byte of src_idx
// with the high bits of (em.size() - 1) and index past the end of
// em for modulus lengths that are not a multiple of 256 bytes
// (e.g. 384-byte EM of a 3072-bit RSA key). The widening is
// branch-free arithmetic: 0x00 -> 0, 0xFF -> ~size_t(0).
size_t in_range = size_t(ge_size(em.size() - 1, src_idx)); // 0x00 or 0xFF
size_t mask = size_t(0) - (in_range & size_t(0x01)); // 0 or ~size_t(0)
size_t safe_idx = (src_idx & mask) | ((em.size() - 1) & ~mask);
uint8_t real = em[safe_idx];
uint8_t synthetic = synth[i];
Expand Down
Loading
Loading